cycle-based RTC
This commit is contained in:
parent
d4f42d9f32
commit
7d33d604ae
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_);
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
||||
|
|
|
@ -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_);
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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_);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
Loading…
Reference in New Issue