diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs index 3776eb00f7..218d2bf3ec 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs @@ -41,31 +41,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy break; default: throw new ArgumentException("Size of saveram data does not match expected!"); - case 44: - data = FixRTC(data, 44); - break; - case 40: - data = FixRTC(data, 40); - break; } LibGambatte.gambatte_loadsavedata(GambatteState, data); } - - private byte[] FixRTC(byte[] data, int offset) - { - // length - offset is the start of the VBA-only data; so - // length - offset - 4 is the start of the RTC block - int idx = data.Length - offset - 4; - - byte[] ret = new byte[idx + 4]; - Buffer.BlockCopy(data, 0, ret, 0, idx); - data[idx] = (byte)zerotime; - data[idx + 1] = (byte)(zerotime >> 8); - data[idx + 2] = (byte)(zerotime >> 16); - data[idx + 3] = (byte)(zerotime >> 24); - - return ret; - } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs index 297b029f11..3681c532d3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs @@ -116,18 +116,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy [DefaultValue(false)] public bool RealTimeRTC { get; set; } - [DisplayName("RTC Initial Time")] - [Description("Set the initial RTC time in terms of elapsed seconds. Only used when RealTimeRTC is false.")] - [DefaultValue(0)] - public int RTCInitialTime - { - get { return _RTCInitialTime; } - set { _RTCInitialTime = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value)); } - } - - [JsonIgnore] - private int _RTCInitialTime; - [DisplayName("Equal Length Frames")] [Description("When false, emulation frames sync to vblank. Only useful for high level TASing.")] [DefaultValue(false)] @@ -141,11 +129,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy [DeepEqualsIgnore] private bool _equalLengthFrames; - [DisplayName("Initial DIV offset")] - [Description("Internal. Probably doesn't work. Leave this set to 0. Accepts values from 0 to 65532 in steps of 4")] - [DefaultValue(0)] - public int InitialDiv { get; set; } - public GambatteSyncSettings() { SettingsUtil.SetDefaultValues(this); @@ -160,11 +143,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy { return !DeepEquality.DeepEquals(x, y); } - - public uint GetInitialDivInternal() - { - return (uint)(InitialDiv & 0xfffc); - } } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs index 1790f04703..3b837f5b78 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -58,13 +58,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy { _syncSettings = (GambatteSyncSettings)syncSettings ?? new GambatteSyncSettings(); - // copy over non-loadflag syncsettings now; they won't take effect if changed later - zerotime = (uint)_syncSettings.RTCInitialTime; - - real_rtc_time = !DeterministicEmulation && _syncSettings.RealTimeRTC; - - DivInternal = _syncSettings.GetInitialDivInternal(); - LibGambatte.LoadFlags flags = 0; switch (_syncSettings.ConsoleMode) @@ -90,7 +83,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy flags |= LibGambatte.LoadFlags.MULTICART_COMPAT; } - if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, GetCurrentTime(), flags, DivInternal) != 0) + if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, flags) != 0) { throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_load)}() returned non-zero (is this not a gb or gbc rom?)"); } @@ -133,8 +126,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy string romname = System.Text.Encoding.ASCII.GetString(buff); Console.WriteLine("Core reported rom name: {0}", romname); - TimeCallback = new LibGambatte.RTCCallback(GetCurrentTime); - LibGambatte.gambatte_setrtccallback(GambatteState, TimeCallback); + if (!DeterministicEmulation && _syncSettings.RealTimeRTC) + { + LibGambatte.gambatte_settimemode(GambatteState, false); + } _cdCallback = new LibGambatte.CDCallback(CDCallbackProc); @@ -167,59 +162,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy /// private LibGambatte.Buttons CurrentButtons = 0; - private uint DivInternal = 0; - - #region RTC - - /// - /// RTC time when emulation begins. - /// - private readonly uint zerotime = 0; - - /// - /// if true, RTC will run off of real elapsed time - /// - private bool real_rtc_time = false; - - private LibGambatte.RTCCallback TimeCallback; - - private static long GetUnixNow() - { - // because internally the RTC works off of relative time, we don't need to base - // this off of any particular canonical epoch. - return DateTime.UtcNow.Ticks / 10000000L - 60000000000L; - } - - private uint GetCurrentTime() - { - if (real_rtc_time) - { - return (uint)GetUnixNow(); - } - - ulong fn = (ulong)Frame; - - // as we're exactly tracking cpu cycles, this can be pretty accurate - fn *= 4389; - fn /= 262144; - fn += zerotime; - return (uint)fn; - } - - private uint GetInitialTime() - { - if (real_rtc_time) - { - return (uint)GetUnixNow(); - } - - // setting the initial boot time to 0 will cause our zerotime - // to function as an initial offset, which is what we want - return 0; - } - - #endregion - #region ALL SAVESTATEABLE STATE GOES HERE /// @@ -320,7 +262,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy if (controller.IsPressed("Power")) { - LibGambatte.gambatte_reset(GambatteState, GetCurrentTime(), DivInternal); + LibGambatte.gambatte_reset(GambatteState); } if (Tracer.Enabled) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs index 06cb95ece3..038ff62021 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs @@ -53,11 +53,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy /// 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, long now, LoadFlags flags, uint div); + public static extern int gambatte_load(IntPtr core, byte[] romdata, uint length, LoadFlags flags); /// /// Load GB(C) BIOS image. @@ -114,9 +113,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy /// 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, long now, uint div); + public static extern void gambatte_reset(IntPtr core); /// /// palette type for gambatte_setdmgpalettecolor @@ -259,21 +257,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy /// 0-153 inclusive [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 uint 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); /// /// type of the link data sent callback @@ -289,6 +272,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void gambatte_setlinkcallback(IntPtr core, LinkCallback callback); + /// + /// Changes between cycle-based and real-time RTC. Defaults to cycle-based. + /// + /// opaque state pointer + /// use cycle-based RTC + [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void gambatte_settimemode(IntPtr core, bool useCycles); + /// /// Returns true if the currently loaded ROM image is treated as having CGB support. /// diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 8bebff11bc..d86187c0fd 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -64,7 +64,7 @@ public: * @param flags ORed combination of LoadFlags. * @return 0 on success, negative value on failure. */ - LoadRes load(char const *romfiledata, unsigned romfilelength, std::uint32_t now, unsigned flags, unsigned div); + LoadRes load(char const *romfiledata, unsigned romfilelength, unsigned flags); int loadBios(char const *biosfiledata, std::size_t size); @@ -106,7 +106,7 @@ public: * Reset to initial state. * Equivalent to reloading a ROM image, or turning a Game Boy Color off and on again. */ - void reset(std::uint32_t now, unsigned div); + void reset(); /** * @param palNum 0 <= palNum < 3. One of BG_PALETTE, SP1_PALETTE and SP2_PALETTE. @@ -128,6 +128,9 @@ public: void setRTCCallback(std::uint32_t (*callback)()); void setLinkCallback(void(*callback)()); + /** Use cycle-based RTC instead of real-time. */ + void setTimeMode(bool useCycles); + /** Returns true if the currently loaded ROM image is treated as having CGB support. */ bool isCgb() const; diff --git a/libgambatte/libgambatte.vcxproj b/libgambatte/libgambatte.vcxproj index 33e0eb0aee..6612bff667 100644 --- a/libgambatte/libgambatte.vcxproj +++ b/libgambatte/libgambatte.vcxproj @@ -170,6 +170,7 @@ + @@ -202,6 +203,7 @@ + diff --git a/libgambatte/libgambatte.vcxproj.filters b/libgambatte/libgambatte.vcxproj.filters index 38cbfcdb7d..f096a22f6b 100644 --- a/libgambatte/libgambatte.vcxproj.filters +++ b/libgambatte/libgambatte.vcxproj.filters @@ -126,6 +126,9 @@ Header Files + + Header Files + @@ -203,5 +206,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index c342d81418..51c4730ae1 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -45,8 +45,8 @@ GBEXPORT void gambatte_destroy(GB *g) { delete g; } -GBEXPORT int gambatte_load(GB *g, char const *romfiledata, unsigned romfilelength, long long now, unsigned flags, unsigned div) { - return g->load(romfiledata, romfilelength, now, flags, div); +GBEXPORT int gambatte_load(GB *g, char const *romfiledata, unsigned romfilelength, unsigned flags) { + return g->load(romfiledata, romfilelength, flags); } GBEXPORT int gambatte_loadbios(GB *g, char const *biosfiledata, unsigned size) { @@ -68,8 +68,12 @@ GBEXPORT void gambatte_setlayers(GB *g, unsigned mask) { g->setLayers(mask); } -GBEXPORT void gambatte_reset(GB *g, long long now, unsigned div) { - g->reset(now, div); +GBEXPORT void gambatte_setTimeMode(GB *g, bool useCycles) { + g->setTimeMode(useCycles); +} + +GBEXPORT void gambatte_reset(GB *g) { + g->reset(); } GBEXPORT void gambatte_setdmgpalettecolor(GB *g, unsigned palnum, unsigned colornum, unsigned rgb32) { @@ -109,10 +113,6 @@ GBEXPORT void gambatte_setscanlinecallback(GB *g, void (*callback)(), int sl) { g->setScanlineCallback(callback, sl); } -GBEXPORT void gambatte_setrtccallback(GB *g, unsigned int (*callback)()) { - g->setRTCCallback(callback); -} - GBEXPORT void gambatte_setlinkcallback(GB *g, void(*callback)()) { g->setLinkCallback(callback); } diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 5349514e05..6059b0fad4 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -31,9 +31,9 @@ public: void setStatePtrs(SaveState &state); void loadState(SaveState const &state); void setLayers(unsigned mask) { mem_.setLayers(mask); } - void loadSavedata(char const *data) { mem_.loadSavedata(data); } + void loadSavedata(char const *data) { mem_.loadSavedata(data, cycleCounter_); } int saveSavedataLength() {return mem_.saveSavedataLength(); } - void saveSavedata(char *dest) { mem_.saveSavedata(dest); } + void saveSavedata(char *dest) { mem_.saveSavedata(dest, cycleCounter_); } bool getMemoryArea(int which, unsigned char **data, int *length) { return mem_.getMemoryArea(which, data, length); } @@ -69,10 +69,6 @@ public: mem_.setScanlineCallback(callback, sl); } - void setRTCCallback(std::uint32_t (*callback)()) { - mem_.setRTCCallback(callback); - } - void setLinkCallback(void(*callback)()) { mem_.setLinkCallback(callback); } @@ -94,6 +90,7 @@ public: void setCgbPalette(unsigned *lut) { mem_.setCgbPalette(lut); } + void setTimeMode(bool useCycles) { mem_.setTimeMode(useCycles, cycleCounter_); } void setBios(char const *buffer, std::size_t size) { mem_.setBios(buffer, size); } bool gbIsCgb() { return mem_.gbIsCgb(); } diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index d4beddafce..d222874ac6 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -75,7 +75,7 @@ void GB::blitTo(gambatte::uint_least32_t *videoBuf, std::ptrdiff_t pitch) { } } -void GB::reset(const std::uint32_t now, const unsigned div) { +void GB::reset() { if (p_->cpu.loaded()) { int length = p_->cpu.saveSavedataLength(); @@ -88,7 +88,7 @@ void GB::reset(const std::uint32_t now, const unsigned div) { SaveState state; p_->cpu.setStatePtrs(state); - setInitState(state, !(p_->loadflags & FORCE_DMG), p_->loadflags & GBA_CGB, now, div); + setInitState(state, !(p_->loadflags & FORCE_DMG), p_->loadflags & GBA_CGB); p_->cpu.loadState(state); if (length > 0) { @@ -126,22 +126,22 @@ void GB::setScanlineCallback(void (*callback)(), int sl) { p_->cpu.setScanlineCallback(callback, sl); } -void GB::setRTCCallback(std::uint32_t (*callback)()) { - p_->cpu.setRTCCallback(callback); -} - void GB::setLinkCallback(void(*callback)()) { p_->cpu.setLinkCallback(callback); } -LoadRes GB::load(char const *romfiledata, unsigned romfilelength, const std::uint32_t now, unsigned const flags, const unsigned div) { +void GB::setTimeMode(bool useCycles) { + p_->cpu.setTimeMode(useCycles); +} + +LoadRes GB::load(char const *romfiledata, unsigned romfilelength, unsigned const flags) { LoadRes const loadres = p_->cpu.load(romfiledata, romfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT); if (loadres == LOADRES_OK) { SaveState state; p_->cpu.setStatePtrs(state); p_->loadflags = flags; - setInitState(state, !(flags & FORCE_DMG), flags & GBA_CGB, now, div); + setInitState(state, !(flags & FORCE_DMG), flags & GBA_CGB); p_->cpu.loadState(state); } diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index d147eb03d6..72e26c6f85 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -20,6 +20,7 @@ #include "counterdef.h" #include "savestate.h" #include "sound/sound_unit.h" +#include "mem/time.h" #include #include @@ -1146,7 +1147,7 @@ static void setInitialDmgIoamhram(unsigned char ioamhram[]) { } // anon namespace -void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode, const std::uint32_t now, const unsigned div) { +void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode) { static unsigned char const cgbObjpDump[0x40] = { 0x00, 0x00, 0xF2, 0xAB, 0x61, 0xC2, 0xD9, 0xBA, @@ -1198,7 +1199,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.mem.ioamhram.ptr[0x140] = 0; state.mem.ioamhram.ptr[0x144] = 0x00; - state.mem.divLastUpdate = 0 - div; + state.mem.divLastUpdate = 0; state.mem.timaBasetime = 0; state.mem.timaLastUpdate = 0; state.mem.tmatime = disabled_time; @@ -1315,8 +1316,12 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.spu.ch4.nr4 = 0; state.spu.ch4.master = false; - state.rtc.baseTime = now; - state.rtc.haltTime = state.rtc.baseTime; + state.time.seconds = 0; + state.time.lastTimeSec = Time::now().tv_sec; + state.time.lastTimeUsec = Time::now().tv_usec; + state.time.lastCycles = state.cpu.cycleCounter; + + state.rtc.haltTime = state.time.seconds; state.rtc.dataDh = 0; state.rtc.dataDl = 0; state.rtc.dataH = 0; diff --git a/libgambatte/src/initstate.h b/libgambatte/src/initstate.h index 02363941d8..0e31dd35bb 100644 --- a/libgambatte/src/initstate.h +++ b/libgambatte/src/initstate.h @@ -23,7 +23,7 @@ namespace gambatte { -void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode, std::uint32_t now, unsigned div); +void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode); } #endif diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index 5465548f22..0d6539af68 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -50,7 +50,7 @@ public: { } - virtual void romWrite(unsigned const p, unsigned const data) { + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { if (p < 0x2000) { enableRam_ = (data & 0xF) == 0xA; memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0); @@ -92,7 +92,7 @@ public: { } - virtual void romWrite(unsigned const p, unsigned const data) { + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p >> 13 & 3) { case 0: enableRam_ = (data & 0xF) == 0xA; @@ -166,7 +166,7 @@ public: { } - virtual void romWrite(unsigned const p, unsigned const data) { + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p >> 13 & 3) { case 0: enableRam_ = (data & 0xF) == 0xA; @@ -238,7 +238,7 @@ public: { } - virtual void romWrite(unsigned const p, unsigned const data) { + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p & 0x6100) { case 0x0000: enableRam_ = (data & 0xF) == 0xA; @@ -282,7 +282,7 @@ public: { } - virtual void romWrite(unsigned const p, unsigned const data) { + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const cc) { switch (p >> 13 & 3) { case 0: enableRam_ = (data & 0xF) == 0xA; @@ -298,7 +298,7 @@ public: break; case 3: if (rtc_) - rtc_->latch(data); + rtc_->latch(data, cc); break; } @@ -356,7 +356,7 @@ public: { } - virtual void romWrite(unsigned const p, unsigned const data) { + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p >> 13 & 3) { case 0: enableRam_ = (data & 0xF) == 0xA; @@ -424,7 +424,7 @@ public: { } - virtual void romWrite(unsigned const p, unsigned const data) { + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p >> 13 & 3) { case 0: enableRam_ = (data & 0xF) == 0xA; @@ -487,6 +487,11 @@ static bool hasRtc(unsigned headerByte0x147) { } +Cartridge::Cartridge() +: rtc_(time_) +{ +} + void Cartridge::setStatePtrs(SaveState &state) { state.mem.vram.set(memptrs_.vramdata(), memptrs_.vramdataend() - memptrs_.vramdata()); state.mem.sram.set(memptrs_.rambankdata(), memptrs_.rambankdataend() - memptrs_.rambankdata()); @@ -652,7 +657,7 @@ static bool hasBattery(unsigned char headerByte0x147) { } } -void Cartridge::loadSavedata(char const *data) { +void Cartridge::loadSavedata(char const *data, unsigned long const cc) { if (hasBattery(memptrs_.romdata()[0x147])) { int length = memptrs_.rambankdataend() - memptrs_.rambankdata(); std::memcpy(memptrs_.rambankdata(), data, length); @@ -661,9 +666,17 @@ void Cartridge::loadSavedata(char const *data) { } if (hasRtc(memptrs_.romdata()[0x147])) { - unsigned long basetime; - std::memcpy(&basetime, data, 4); - rtc_.setBaseTime(basetime); + timeval basetime; + basetime.tv_sec = (*data++); + basetime.tv_sec = basetime.tv_sec << 8 | (*data++); + basetime.tv_sec = basetime.tv_sec << 8 | (*data++); + basetime.tv_sec = basetime.tv_sec << 8 | (*data++); + basetime.tv_usec = (*data++); + basetime.tv_usec = basetime.tv_usec << 8 | (*data++); + basetime.tv_usec = basetime.tv_usec << 8 | (*data++); + basetime.tv_usec = basetime.tv_usec << 8 | (*data++); + + time_.setBaseTime(basetime, cc); } } @@ -673,12 +686,12 @@ int Cartridge::saveSavedataLength() { ret = memptrs_.rambankdataend() - memptrs_.rambankdata(); } if (hasRtc(memptrs_.romdata()[0x147])) { - ret += 4; + ret += 8; } return ret; } -void Cartridge::saveSavedata(char *dest) { +void Cartridge::saveSavedata(char *dest, unsigned long const cc) { if (hasBattery(memptrs_.romdata()[0x147])) { int length = memptrs_.rambankdataend() - memptrs_.rambankdata(); std::memcpy(dest, memptrs_.rambankdata(), length); @@ -686,8 +699,15 @@ void Cartridge::saveSavedata(char *dest) { } if (hasRtc(memptrs_.romdata()[0x147])) { - const unsigned long basetime = rtc_.getBaseTime(); - std::memcpy(dest, &basetime, 4); + timeval basetime = time_.baseTime(cc); + *dest++ = (basetime.tv_sec >> 24 & 0xFF); + *dest++ = (basetime.tv_sec >> 16 & 0xFF); + *dest++ = (basetime.tv_sec >> 8 & 0xFF); + *dest++ = (basetime.tv_sec & 0xFF); + *dest++ = (basetime.tv_usec >> 24 & 0xFF); + *dest++ = (basetime.tv_usec >> 16 & 0xFF); + *dest++ = (basetime.tv_usec >> 8 & 0xFF); + *dest++ = (basetime.tv_usec & 0xFF); } } @@ -723,6 +743,7 @@ bool Cartridge::getMemoryArea(int which, unsigned char **data, int *length) cons SYNCFUNC(Cartridge) { SSS(memptrs_); + SSS(time_); SSS(rtc_); TSS(mbc_); } diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h index 245bf537aa..db46afb262 100644 --- a/libgambatte/src/mem/cartridge.h +++ b/libgambatte/src/mem/cartridge.h @@ -21,6 +21,7 @@ #include "loadres.h" #include "memptrs.h" +#include "time.h" #include "rtc.h" #include "savestate.h" #include @@ -33,7 +34,7 @@ namespace gambatte { class Mbc { public: virtual ~Mbc() {} - virtual void romWrite(unsigned P, unsigned data) = 0; + virtual void romWrite(unsigned P, unsigned data, unsigned long cycleCounter) = 0; virtual void loadState(SaveState::Mem const &ss) = 0; virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned address, unsigned rombank) const = 0; @@ -47,6 +48,7 @@ public: class Cartridge { public: + Cartridge(); void setStatePtrs(SaveState &); void loadState(SaveState const &); bool loaded() const { return mbc_.get(); } @@ -64,23 +66,23 @@ public: void setWrambank(unsigned bank) { memptrs_.setWrambank(bank); } void setOamDmaSrc(OamDmaSrc oamDmaSrc) { memptrs_.setOamDmaSrc(oamDmaSrc); } unsigned curRomBank() const { return memptrs_.curRomBank(); } - void mbcWrite(unsigned addr, unsigned data) { mbc_->romWrite(addr, data); } + void mbcWrite(unsigned addr, unsigned data, unsigned long const cc) { mbc_->romWrite(addr, data, cc); } bool isCgb() const { return gambatte::isCgb(memptrs_); } - void rtcWrite(unsigned data) { rtc_.write(data); } + void resetCc(unsigned long const oldCc, unsigned long const newCc) { time_.resetCc(oldCc, newCc); } + void speedChange(unsigned long const cc) { time_.speedChange(cc); } + void setTimeMode(bool useCycles, unsigned long const cc) { time_.setTimeMode(useCycles, cc); } + void rtcWrite(unsigned data, unsigned long const cc) { rtc_.write(data, cc); } unsigned char rtcRead() const { return *rtc_.activeData(); } - void loadSavedata(char const *data); + void loadSavedata(char const *data, unsigned long cycleCounter); int saveSavedataLength(); - void saveSavedata(char *dest); + void saveSavedata(char *dest, unsigned long cycleCounter); bool getMemoryArea(int which, unsigned char **data, int *length) const; LoadRes loadROM(char const *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); char const * romTitle() const { return reinterpret_cast(memptrs_.romdata() + 0x134); } - void setRTCCallback(std::uint32_t (*callback)()) { - rtc_.setRTCCallback(callback); - } - private: MemPtrs memptrs_; + Time time_; Rtc rtc_; std::unique_ptr mbc_; diff --git a/libgambatte/src/mem/rtc.cpp b/libgambatte/src/mem/rtc.cpp index 5495201c4c..5b79c04d7b 100644 --- a/libgambatte/src/mem/rtc.cpp +++ b/libgambatte/src/mem/rtc.cpp @@ -22,10 +22,10 @@ namespace gambatte { -Rtc::Rtc() -: activeData_(0) +Rtc::Rtc(Time &time) +: time_(time) +, activeData_(0) , activeSet_(0) -, baseTime_(0) , haltTime_(0) , index_(5) , dataDh_(0) @@ -35,16 +35,15 @@ Rtc::Rtc() , dataS_(0) , enabled_(false) , lastLatchData_(false) -, timeCB(0) { } -void Rtc::doLatch() { - std::uint32_t tmp = ((dataDh_ & 0x40) ? haltTime_ : timeCB()) - baseTime_; +void Rtc::doLatch(unsigned long const cc) { + std::uint32_t tmp = time(cc); - while (tmp > 0x1FF * 86400) { - baseTime_ += 0x1FF * 86400; - tmp -= 0x1FF * 86400; + if (tmp >= 0x200 * 86400) { + tmp %= 0x200 * 86400; + time_.set(tmp, cc); dataDh_ |= 0x80; } @@ -91,7 +90,6 @@ void Rtc::doSwapActive() { } void Rtc::loadState(SaveState const &state) { - baseTime_ = state.rtc.baseTime; haltTime_ = state.rtc.haltTime; dataDh_ = state.rtc.dataDh; dataDl_ = state.rtc.dataDl; @@ -102,45 +100,50 @@ void Rtc::loadState(SaveState const &state) { doSwapActive(); } -void Rtc::setDh(unsigned const newDh) { - const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); - const std::uint32_t oldHighdays = ((unixtime - baseTime_) / 86400) & 0x100; - baseTime_ += oldHighdays * 86400; - baseTime_ -= ((newDh & 0x1) << 8) * 86400; +void Rtc::setDh(unsigned const newDh, unsigned const long cc) { + std::uint32_t seconds = time(cc); + std::uint32_t const oldHighdays = (seconds / 86400) & 0x100; + seconds -= oldHighdays * 86400; + seconds += ((newDh & 0x1) << 8) * 86400; + time_.set(seconds, cc); if ((dataDh_ ^ newDh) & 0x40) { if (newDh & 0x40) - haltTime_ = timeCB(); + haltTime_ = seconds; else - baseTime_ += timeCB() - haltTime_; + time_.set(haltTime_, cc); } } -void Rtc::setDl(unsigned const newLowdays) { - const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); - const std::uint32_t oldLowdays = ((unixtime - baseTime_) / 86400) & 0xFF; - baseTime_ += oldLowdays * 86400; - baseTime_ -= newLowdays * 86400; +void Rtc::setDl(unsigned const newLowdays, unsigned const long cc) { + std::uint32_t seconds = time(cc); + std::uint32_t const oldLowdays = (seconds / 86400) & 0xFF; + seconds -= oldLowdays * 86400; + seconds += newLowdays * 86400; + time_.set(seconds, cc); } -void Rtc::setH(unsigned const newHours) { - const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); - const std::uint32_t oldHours = ((unixtime - baseTime_) / 3600) % 24; - baseTime_ += oldHours * 3600; - baseTime_ -= newHours * 3600; +void Rtc::setH(unsigned const newHours, unsigned const long cc) { + std::uint32_t seconds = time(cc); + std::uint32_t const oldHours = (seconds / 3600) % 24; + seconds -= oldHours * 3600; + seconds += newHours * 3600; + time_.set(seconds, cc); } -void Rtc::setM(unsigned const newMinutes) { - const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); - const std::uint32_t oldMinutes = ((unixtime - baseTime_) / 60) % 60; - baseTime_ += oldMinutes * 60; - baseTime_ -= newMinutes * 60; +void Rtc::setM(unsigned const newMinutes, unsigned const long cc) { + std::uint32_t seconds = time(cc); + std::uint32_t const oldMinutes = (seconds / 60) % 60; + seconds -= oldMinutes * 60; + seconds += newMinutes * 60; + time_.set(seconds, cc); } -void Rtc::setS(unsigned const newSeconds) { - const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); - baseTime_ += (unixtime - baseTime_) % 60; - baseTime_ -= newSeconds; +void Rtc::setS(unsigned const newSeconds, unsigned const long cc) { + std::uint32_t seconds = time(cc); + seconds -= seconds % 60; + seconds += newSeconds; + time_.reset(seconds, cc); } SYNCFUNC(Rtc) @@ -161,7 +164,6 @@ SYNCFUNC(Rtc) EVS(activeSet_, &Rtc::setDh, 5); EES(activeSet_, NULL); - NSS(baseTime_); NSS(haltTime_); NSS(index_); NSS(dataDh_); diff --git a/libgambatte/src/mem/rtc.h b/libgambatte/src/mem/rtc.h index f7594e2eec..853027ed58 100644 --- a/libgambatte/src/mem/rtc.h +++ b/libgambatte/src/mem/rtc.h @@ -20,6 +20,7 @@ #define RTC_H #include +#include "time.h" #include "newstate.h" namespace gambatte { @@ -28,18 +29,12 @@ struct SaveState; class Rtc { public: - Rtc(); - + Rtc(Time &time); unsigned char const * activeData() const { return activeData_; } - std::uint32_t getBaseTime() const { return baseTime_; } - void setBaseTime(const std::uint32_t baseTime) { - this->baseTime_ = baseTime; - } - - void latch(unsigned data) { + void latch(unsigned data, unsigned long const cc) { if (!lastLatchData_ && data == 1) - doLatch(); + doLatch(cc); lastLatchData_ = data; } @@ -55,19 +50,15 @@ public: doSwapActive(); } - void write(unsigned data) { - (this->*activeSet_)(data); + void write(unsigned data, unsigned long const cc) { + (this->*activeSet_)(data, cc); *activeData_ = data; } - void setRTCCallback(std::uint32_t (*callback)()) { - timeCB = callback; - } - private: + Time &time_; unsigned char *activeData_; - void (Rtc::*activeSet_)(unsigned); - std::uint32_t baseTime_; + void (Rtc::*activeSet_)(unsigned, unsigned long); std::uint32_t haltTime_; unsigned char index_; unsigned char dataDh_; @@ -77,16 +68,18 @@ private: unsigned char dataS_; bool enabled_; bool lastLatchData_; - std::uint32_t (*timeCB)(); - void doLatch(); + void doLatch(unsigned long cycleCounter); void doSwapActive(); - void setDh(unsigned newDh); - void setDl(unsigned newLowdays); - void setH(unsigned newHours); - void setM(unsigned newMinutes); - void setS(unsigned newSeconds); + void setDh(unsigned newDh, unsigned long cycleCounter); + void setDl(unsigned newLowdays, unsigned long cycleCounter); + void setH(unsigned newHours, unsigned long cycleCounter); + void setM(unsigned newMinutes, unsigned long cycleCounter); + void setS(unsigned newSeconds, unsigned long cycleCounter); + std::uint32_t time(unsigned long const cc) { + return dataDh_ & 0x40 ? haltTime_ : time_.get(cc); + } public: templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/mem/time.cpp b/libgambatte/src/mem/time.cpp new file mode 100644 index 0000000000..71e92d1356 --- /dev/null +++ b/libgambatte/src/mem/time.cpp @@ -0,0 +1,144 @@ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + +#include "time.h" +#include "../savestate.h" + +namespace gambatte { + +static timeval operator-(timeval l, timeval r) { + timeval t; + t.tv_sec = l.tv_sec - r.tv_sec; + t.tv_usec = l.tv_usec - r.tv_usec; + if (t.tv_usec < 0) { + t.tv_sec--; + t.tv_usec += 1000000; + } + return t; +} + +Time::Time() +: useCycles_(true) +{ +} + +void Time::loadState(SaveState const &state) { + seconds_ = state.time.seconds; + lastTime_.tv_sec = state.time.lastTimeSec; + lastTime_.tv_usec = state.time.lastTimeUsec; + lastCycles_ = state.time.lastCycles; + ds_ = state.ppu.isCgb & state.mem.ioamhram.get()[0x14D] >> 7; +} + +std::uint32_t Time::get(unsigned long const cc) { + update(cc); + return seconds_; +} + +void Time::set(std::uint32_t seconds, unsigned long const cc) { + update(cc); + seconds_ = seconds; +} + +void Time::reset(std::uint32_t seconds, unsigned long const cc) { + set(seconds, cc); + lastTime_ = now(); + lastCycles_ = cc; +} + +void Time::resetCc(unsigned long const oldCc, unsigned long const newCc) { + update(oldCc); + lastCycles_ -= oldCc - newCc; +} + +void Time::speedChange(unsigned long const cc) { + update(cc); + + if (useCycles_) { + unsigned long diff = cc - lastCycles_; + lastCycles_ = cc - (ds_ ? diff >> 1 : diff << 1); + } + + ds_ = !ds_; +} + +timeval Time::baseTime(unsigned long const cc) { + if (useCycles_) + timeFromCycles(cc); + + timeval baseTime = lastTime_; + baseTime.tv_sec -= seconds_; + return baseTime; +} + +void Time::setBaseTime(timeval baseTime, unsigned long const cc) { + seconds_ = (now() - baseTime).tv_sec; + lastTime_ = baseTime; + lastTime_.tv_sec += seconds_; + + if (useCycles_) + cyclesFromTime(cc); +} + +void Time::setTimeMode(bool useCycles, unsigned long const cc) { + if (useCycles != useCycles_) { + if (useCycles_) + timeFromCycles(cc); + else + cyclesFromTime(cc); + + useCycles_ = useCycles; + } +} + +void Time::update(unsigned long const cc) { + if (useCycles_) { + std::uint32_t diff = (cc - lastCycles_) / (0x400000 << ds_); + seconds_ += diff; + lastCycles_ += diff * (0x400000 << ds_); + } else { + std::uint32_t diff = (now() - lastTime_).tv_sec; + seconds_ += diff; + lastTime_.tv_sec += diff; + } +} + +void Time::cyclesFromTime(unsigned long const cc) { + update(cc); + timeval diff = now() - lastTime_; + lastCycles_ = cc - diff.tv_usec * ((0x400000 << ds_) / 1000000.0f); +} + +void Time::timeFromCycles(unsigned long const cc) { + update(cc); + unsigned long diff = cc - lastCycles_; + timeval usec = { 0, (long)(diff / ((0x400000 << ds_) / 1000000.0f)) }; + lastTime_ = now() - usec; +} + +SYNCFUNC(Time) +{ + NSS(seconds_); + NSS(lastTime_.tv_sec); + NSS(lastTime_.tv_usec); + NSS(lastCycles_); + NSS(useCycles_); + NSS(ds_); +} + +} diff --git a/libgambatte/src/mem/time.h b/libgambatte/src/mem/time.h new file mode 100644 index 0000000000..aa8e49b5e3 --- /dev/null +++ b/libgambatte/src/mem/time.h @@ -0,0 +1,78 @@ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + +#ifndef TIME_H +#define TIME_H + +#include +#include +#include +#include "newstate.h" + +namespace gambatte { + +struct SaveState; + +struct timeval { + std::uint32_t tv_sec; + std::uint32_t tv_usec; +}; + +class Time { +public: + static timeval now() { + long long micros = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count(); + timeval t; + t.tv_usec = micros % 1000000; + t.tv_sec = micros / 1000000; + return t; + } + + Time(); + void loadState(SaveState const &state); + + std::uint32_t get(unsigned long cycleCounter); + void set(std::uint32_t seconds, unsigned long cycleCounter); + void reset(std::uint32_t seconds, unsigned long cycleCounter); + void resetCc(unsigned long oldCc, unsigned long newCc); + void speedChange(unsigned long cycleCounter); + + timeval baseTime(unsigned long cycleCounter); + void setBaseTime(timeval baseTime, unsigned long cycleCounter); + void setTimeMode(bool useCycles, unsigned long cycleCounter); + +private: + std::uint32_t seconds_; + timeval lastTime_; + unsigned long lastCycles_; + bool useCycles_; + bool ds_; + + void update(unsigned long cycleCounter); + void cyclesFromTime(unsigned long cycleCounter); + void timeFromCycles(unsigned long cycleCounter); + +public: + templatevoid SyncState(NewState *ns); +}; + +} + +#endif diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 2e39855eda..6ddce9da05 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -345,6 +345,7 @@ unsigned long Memory::stop(unsigned long cc) { if (ioamhram_[0x14D] & isCgb()) { psg_.generateSamples(cc + 4, isDoubleSpeed()); lcd_.speedChange((cc + 7) & ~7); + cart_.speedChange(cc); ioamhram_[0x14D] ^= 0x81; intreq_.setEventTime(ioamhram_[0x140] & lcdc_en ? lcd_.nextMode1IrqTime() @@ -403,6 +404,7 @@ unsigned long Memory::resetCounters(unsigned long cc) { unsigned long const oldCC = cc; cc -= dec; intreq_.resetCc(oldCC, cc); + cart_.resetCc(oldCC, cc); tima_.resetCc(oldCC, cc, TimaInterruptRequester(intreq_)); lcd_.resetCc(oldCC, cc); psg_.resetCounter(cc, oldCC, isDoubleSpeed()); @@ -1099,7 +1101,7 @@ void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned lo if (p < 0xFE00) { if (p < 0xA000) { if (p < 0x8000) { - cart_.mbcWrite(p, data); + cart_.mbcWrite(p, data, cc); } else if (lcd_.vramAccessible(cc)) { lcd_.vramChange(cc); cart_.vrambankptr()[p] = data; @@ -1108,7 +1110,7 @@ void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned lo if (cart_.wsrambankptr()) cart_.wsrambankptr()[p] = data; else - cart_.rtcWrite(data); + cart_.rtcWrite(data, cc); } else cart_.wramdata(p >> 12 & 1)[p & 0xFFF] = data; } else if (p - 0xFF80u >= 0x7Fu) { diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 051a5ea904..f00e28ac24 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -29,6 +29,7 @@ static unsigned char const agbOverride[0xD] = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0x #include "gambatte.h" namespace gambatte { + class FilterInfo; class Memory { @@ -42,9 +43,9 @@ public: int debugGetLY() const { return lcd_.debugGetLY(); } void setStatePtrs(SaveState &state); void loadState(SaveState const &state); - void loadSavedata(char const *data) { cart_.loadSavedata(data); } + void loadSavedata(char const *data, unsigned long const cc) { cart_.loadSavedata(data, cc); } int saveSavedataLength() {return cart_.saveSavedataLength(); } - void saveSavedata(char *dest) { cart_.saveSavedata(dest); } + void saveSavedata(char *dest, unsigned long const cc) { cart_.saveSavedata(dest, cc); } void updateInput(); void setBios(char const *buffer, std::size_t size) { @@ -243,10 +244,6 @@ public: lcd_.setScanlineCallback(callback, sl); } - void setRTCCallback(std::uint32_t (*callback)()) { - cart_.setRTCCallback(callback); - } - void setLinkCallback(void(*callback)()) { this->linkCallback_ = callback; } @@ -266,6 +263,9 @@ public: } void setCgbPalette(unsigned *lut); + void setTimeMode(bool useCycles, unsigned long const cc) { + cart_.setTimeMode(useCycles, cc); + } int linkStatus(int which); diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index e56290685d..c130ab5ecb 100644 --- a/libgambatte/src/savestate.h +++ b/libgambatte/src/savestate.h @@ -36,7 +36,7 @@ struct SaveState { void set(T *p, std::size_t size) { ptr = p; size_ = size; } friend class SaverList; - friend void setInitState(SaveState &, bool, bool, std::uint32_t, unsigned); + friend void setInitState(SaveState &, bool, bool); private: T *ptr; std::size_t size_; @@ -190,8 +190,14 @@ struct SaveState { unsigned long cycleCounter; } spu; + struct Time { + unsigned long seconds; + unsigned long lastTimeSec; + unsigned long lastTimeUsec; + unsigned long lastCycles; + } time; + struct RTC { - unsigned long baseTime; unsigned long haltTime; unsigned char dataDh; unsigned char dataDl; diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 2d96928fd0..17b09b4e0a 100644 Binary files a/output/dll/libgambatte.dll and b/output/dll/libgambatte.dll differ