cycle-based RTC

This commit is contained in:
MrWint 2019-05-28 22:56:39 +02:00
parent d4f42d9f32
commit 7d33d604ae
22 changed files with 402 additions and 252 deletions

View File

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

View File

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

View File

@ -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
/// </summary>
private LibGambatte.Buttons CurrentButtons = 0;
private uint DivInternal = 0;
#region RTC
/// <summary>
/// RTC time when emulation begins.
/// </summary>
private readonly uint zerotime = 0;
/// <summary>
/// if true, RTC will run off of real elapsed time
/// </summary>
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
/// <summary>
@ -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)

View File

@ -53,11 +53,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
/// <param name="core">opaque state pointer</param>
/// <param name="romdata">the rom data, can be disposed of once this function returns</param>
/// <param name="length">length of romdata in bytes</param>
/// <param name="now">RTC time when the rom is loaded</param>
/// <param name="flags">ORed combination of LoadFlags.</param>
/// <returns>0 on success, negative value on failure.</returns>
[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);
/// <summary>
/// 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.
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="now">RTC time when the reset occurs</param>
[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);
/// <summary>
/// palette type for gambatte_setdmgpalettecolor
@ -259,21 +257,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
/// <param name="sl">0-153 inclusive</param>
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gambatte_setscanlinecallback(IntPtr core, ScanlineCallback callback, int sl);
/// <summary>
/// type of the RTC callback
/// </summary>
/// <returns>what time is it, unixy</returns>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint RTCCallback();
/// <summary>
/// sets RTC callback. probably mandatory.
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="callback">the callback</param>
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gambatte_setrtccallback(IntPtr core, RTCCallback callback);
/// <summary>
/// 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);
/// <summary>
/// Changes between cycle-based and real-time RTC. Defaults to cycle-based.
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="useCycles">use cycle-based RTC</param>
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gambatte_settimemode(IntPtr core, bool useCycles);
/// <summary>
/// Returns true if the currently loaded ROM image is treated as having CGB support.
/// </summary>

View File

@ -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;

View File

@ -170,6 +170,7 @@
<ClInclude Include="src\mem\cartridge.h" />
<ClInclude Include="src\mem\memptrs.h" />
<ClInclude Include="src\mem\rtc.h" />
<ClInclude Include="src\mem\time.h" />
<ClInclude Include="src\minkeeper.h" />
<ClInclude Include="src\newstate.h" />
<ClInclude Include="src\savestate.h" />
@ -202,6 +203,7 @@
<ClCompile Include="src\mem\cartridge.cpp" />
<ClCompile Include="src\mem\memptrs.cpp" />
<ClCompile Include="src\mem\rtc.cpp" />
<ClCompile Include="src\mem\time.cpp" />
<ClCompile Include="src\newstate.cpp" />
<ClCompile Include="src\sound.cpp" />
<ClCompile Include="src\sound\channel1.cpp" />

View File

@ -126,6 +126,9 @@
<ClInclude Include="src\sound\static_output_tester.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\mem\time.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\cinterface.cpp">
@ -203,5 +206,8 @@
<ClCompile Include="src\newstate.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\mem\time.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

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

View File

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

View File

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

View File

@ -20,6 +20,7 @@
#include "counterdef.h"
#include "savestate.h"
#include "sound/sound_unit.h"
#include "mem/time.h"
#include <algorithm>
#include <cstring>
@ -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;

View File

@ -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

View File

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

View File

@ -21,6 +21,7 @@
#include "loadres.h"
#include "memptrs.h"
#include "time.h"
#include "rtc.h"
#include "savestate.h"
#include <memory>
@ -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<char const *>(memptrs_.romdata() + 0x134); }
void setRTCCallback(std::uint32_t (*callback)()) {
rtc_.setRTCCallback(callback);
}
private:
MemPtrs memptrs_;
Time time_;
Rtc rtc_;
std::unique_ptr<Mbc> mbc_;

View File

@ -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_);

View File

@ -20,6 +20,7 @@
#define RTC_H
#include <cstdint>
#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:
template<bool isReader>void SyncState(NewState *ns);
};

View File

@ -0,0 +1,144 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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_);
}
}

View File

@ -0,0 +1,78 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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 <chrono>
#include <cstdint>
#include <ctime>
#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::microseconds>(
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:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -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<intevent_blit>(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) {

View File

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

View File

@ -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;

Binary file not shown.