GB: rtc is pegged to emulated elapsed time. maybe. don't know what to test it with. time at core restart is always 1970, or something like that, which isn't as bad as it sounds, because the GB rtc doesn't keep track of anything past days

This commit is contained in:
goyuken 2013-05-09 23:15:59 +00:00
parent 7e71898187
commit 99615d9afd
15 changed files with 100 additions and 29 deletions

View File

@ -24,6 +24,21 @@ namespace BizHawk.Emulation.Consoles.GB
/// </summary>
LibGambatte.Buttons CurrentButtons = 0;
/// <summary>
/// RTC time when emulation begins.
/// </summary>
long zerotime;
LibGambatte.RTCCallback TimeCallback;
long GetCurrentTime()
{
long fn = Frame;
fn /= 60; // exactly 60 fps. in case you feel bad about it, remember that we're not exactly tracking cpu cycles either.
fn += zerotime;
return fn;
}
public Gameboy(CoreComm comm, GameInfo game, byte[] romdata)
{
CoreComm = comm;
@ -53,7 +68,7 @@ namespace BizHawk.Emulation.Consoles.GB
flags |= LibGambatte.LoadFlags.MULTICART_COMPAT;
if (LibGambatte.gambatte_load(GambatteState, romdata, (uint)romdata.Length, flags) != 0)
if (LibGambatte.gambatte_load(GambatteState, romdata, (uint)romdata.Length, GetCurrentTime(), flags) != 0)
throw new Exception("gambatte_load() returned non-zero (is this not a gb or gbc rom?)");
// set real default colors (before anyone mucks with them at all)
@ -77,6 +92,9 @@ namespace BizHawk.Emulation.Consoles.GB
Util.BytesToHexString(System.Security.Cryptography.SHA1.Create().ComputeHash(romdata)),
Util.BytesToHexString(System.Security.Cryptography.MD5.Create().ComputeHash(romdata))
);
TimeCallback = new LibGambatte.RTCCallback(GetCurrentTime);
LibGambatte.gambatte_setrtccallback(GambatteState, TimeCallback);
}
public static readonly ControllerDefinition GbController = new ControllerDefinition
@ -143,7 +161,7 @@ namespace BizHawk.Emulation.Consoles.GB
r.RefreshWrite();
if (Controller["Power"])
LibGambatte.gambatte_reset(GambatteState);
LibGambatte.gambatte_reset(GambatteState, GetCurrentTime());
RefreshMemoryCallbacks();
if (CoreComm.Tracer.Enabled)

View File

@ -39,10 +39,11 @@ namespace BizHawk.Emulation.Consoles.GB
/// <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, LoadFlags flags);
public static extern int gambatte_load(IntPtr core, byte[] romdata, uint length, long now, LoadFlags flags);
/// <summary>
/// Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer,
@ -74,8 +75,9 @@ namespace BizHawk.Emulation.Consoles.GB
/// Equivalent to reloading a ROM image, or turning a Game Boy Color off and on again.
/// </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);
public static extern void gambatte_reset(IntPtr core, long now);
/// <summary>
/// palette type for gambatte_setdmgpalettecolor
@ -191,6 +193,21 @@ namespace BizHawk.Emulation.Consoles.GB
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gambatte_setscanlinecallback(IntPtr core, ScanlineCallback callback, int sl);
/// <summary>
/// type of the RTC callback
/// </summary>
/// <returns>what time is it, unixy</returns>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate long 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>
/// Sets the directory used for storing save data. The default is the same directory as the ROM Image file.
/// </summary>

View File

@ -23,6 +23,7 @@
#include "gbint.h"
#include <string>
#include <sstream>
#include <ctime>
namespace gambatte {
enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 };
@ -44,7 +45,7 @@ public:
* @param flags ORed combination of LoadFlags.
* @return 0 on success, negative value on failure.
*/
int load(const char *romfiledata, unsigned romfilelength, unsigned flags = 0);
int load(const char *romfiledata, unsigned romfilelength, std::time_t now, unsigned flags = 0);
/** Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer,
* or until a video frame has been drawn.
@ -73,7 +74,7 @@ public:
/** Reset to initial state.
* Equivalent to reloading a ROM image, or turning a Game Boy Color off and on again.
*/
void reset();
void reset(std::time_t now);
/** @param palNum 0 <= palNum < 3. One of BG_PALETTE, SP1_PALETTE and SP2_PALETTE.
* @param colorNum 0 <= colorNum < 4
@ -89,6 +90,7 @@ public:
void setWriteCallback(void (*callback)(unsigned));
void setTraceCallback(void (*callback)(void *));
void setScanlineCallback(void (*callback)(), int sl);
void setRTCCallback(std::time_t (*callback)());
/** Sets the directory used for storing save data. The default is the same directory as the ROM Image file. */
void setSaveDir(const std::string &sdir);

View File

@ -17,10 +17,10 @@ __declspec(dllexport) void gambatte_destroy(void *core)
delete g;
}
__declspec(dllexport) int gambatte_load(void *core, const char *romfiledata, unsigned romfilelength, unsigned flags)
__declspec(dllexport) int gambatte_load(void *core, const char *romfiledata, unsigned romfilelength, long long now, unsigned flags)
{
GB *g = (GB *) core;
int ret = g->load(romfiledata, romfilelength, flags);
int ret = g->load(romfiledata, romfilelength, now, flags);
return ret;
}
@ -33,10 +33,10 @@ __declspec(dllexport) long gambatte_runfor(void *core, unsigned long *videobuf,
return ret;
}
__declspec(dllexport) void gambatte_reset(void *core)
__declspec(dllexport) void gambatte_reset(void *core, long long now)
{
GB *g = (GB *) core;
g->reset();
g->reset(now);
}
__declspec(dllexport) void gambatte_setdmgpalettecolor(void *core, unsigned palnum, unsigned colornum, unsigned rgb32)
@ -95,6 +95,12 @@ __declspec(dllexport) void gambatte_setscanlinecallback(void *core, void (*callb
g->setScanlineCallback(callback, sl);
}
__declspec(dllexport) void gambatte_setrtccallback(void *core, long long (*callback)())
{
GB *g = (GB *) core;
g->setRTCCallback(callback);
}
__declspec(dllexport) void gambatte_setsavedir(void *core, const char *sdir)
{
GB *g = (GB *) core;

View File

@ -8,11 +8,11 @@ extern "C"
__declspec(dllexport) void *gambatte_create();
__declspec(dllexport) void gambatte_destroy(void *core);
__declspec(dllexport) int gambatte_load(void *core, const char *romfiledata, unsigned romfilelength, unsigned flags);
__declspec(dllexport) int gambatte_load(void *core, const char *romfiledata, unsigned romfilelength, long long now, unsigned flags);
__declspec(dllexport) long gambatte_runfor(void *core, unsigned long *videobuf, int pitch, short *soundbuf, unsigned *samples);
__declspec(dllexport) void gambatte_reset(void *core);
__declspec(dllexport) void gambatte_reset(void *core, long long now);
__declspec(dllexport) void gambatte_setdmgpalettecolor(void *core, unsigned palnum, unsigned colornum, unsigned rgb32);
@ -28,6 +28,8 @@ extern "C"
__declspec(dllexport) void gambatte_setscanlinecallback(void *core, void (*callback)(), int sl);
__declspec(dllexport) void gambatte_setrtccallback(void *core, long long (*callback)());
__declspec(dllexport) void gambatte_setsavedir(void *core, const char *sdir);
__declspec(dllexport) int gambatte_iscgb(void *core);

View File

@ -82,6 +82,10 @@ public:
void setScanlineCallback(void (*callback)(), int sl) {
memory.setScanlineCallback(callback, sl);
}
void setRTCCallback(std::time_t (*callback)()) {
memory.setRTCCallback(callback);
}
void setSaveDir(const std::string &sdir) {
memory.setSaveDir(sdir);

View File

@ -68,7 +68,7 @@ long GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch,
return cyclesSinceBlit < 0 ? cyclesSinceBlit : static_cast<long>(samples) - (cyclesSinceBlit >> 1);
}
void GB::reset() {
void GB::reset(const std::time_t now) {
if (p_->cpu.loaded()) {
int length = p_->cpu.saveSavedataLength();
@ -81,7 +81,7 @@ void GB::reset() {
SaveState state;
p_->cpu.setStatePtrs(state);
setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode);
setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode, now);
p_->cpu.loadState(state);
if (length > 0)
{
@ -111,11 +111,15 @@ void GB::setScanlineCallback(void (*callback)(), int sl) {
p_->cpu.setScanlineCallback(callback, sl);
}
void GB::setRTCCallback(long long (*callback)()) {
p_->cpu.setRTCCallback(callback);
}
void GB::setSaveDir(const std::string &sdir) {
p_->cpu.setSaveDir(sdir);
}
int GB::load(const char *romfiledata, unsigned romfilelength, const unsigned flags) {
int GB::load(const char *romfiledata, unsigned romfilelength, const std::time_t now, const unsigned flags) {
//if (p_->cpu.loaded())
// p_->cpu.saveSavedata();
@ -124,7 +128,7 @@ int GB::load(const char *romfiledata, unsigned romfilelength, const unsigned fla
if (!failed) {
SaveState state;
p_->cpu.setStatePtrs(state);
setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode = flags & GBA_CGB);
setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode = flags & GBA_CGB, now);
p_->cpu.loadState(state);
//p_->cpu.loadSavedata();

View File

@ -1147,7 +1147,7 @@ static void setInitialDmgIoamhram(unsigned char *const ioamhram) {
} // anon namespace
void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode) {
void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode, const std::time_t now) {
static const unsigned char cgbObjpDump[0x40] = {
0x00, 0x00, 0xF2, 0xAB,
0x61, 0xC2, 0xD9, 0xBA,
@ -1308,7 +1308,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
state.spu.ch4.nr4 = 0;
state.spu.ch4.master = false;
state.rtc.baseTime = std::time(0);
state.rtc.baseTime = now;
state.rtc.haltTime = state.rtc.baseTime;
state.rtc.dataDh = 0;
state.rtc.dataDl = 0;

View File

@ -19,8 +19,10 @@
#ifndef INITSTATE_H
#define INITSTATE_H
#include <ctime>
namespace gambatte {
void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode);
void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode, std::time_t now);
}
#endif

View File

@ -93,6 +93,10 @@ public:
int loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat);
const char * romTitle() const { return reinterpret_cast<const char *>(memptrs.romdata() + 0x134); }
void setGameGenie(const std::string &codes);
void setRTCCallback(std::time_t (*callback)()) {
rtc.setRTCCallback(callback);
}
};
}

View File

@ -33,12 +33,13 @@ Rtc::Rtc()
dataM(0),
dataS(0),
enabled(false),
lastLatchData(false)
lastLatchData(false),
timeCB(0)
{
}
void Rtc::doLatch() {
std::time_t tmp = ((dataDh & 0x40) ? haltTime : std::time(0)) - baseTime;
std::time_t tmp = ((dataDh & 0x40) ? haltTime : timeCB()) - baseTime;
while (tmp > 0x1FF * 86400) {
baseTime += 0x1FF * 86400;
@ -113,42 +114,42 @@ void Rtc::loadState(const SaveState &state) {
}
void Rtc::setDh(const unsigned new_dh) {
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : timeCB();
const std::time_t old_highdays = ((unixtime - baseTime) / 86400) & 0x100;
baseTime += old_highdays * 86400;
baseTime -= ((new_dh & 0x1) << 8) * 86400;
if ((dataDh ^ new_dh) & 0x40) {
if (new_dh & 0x40)
haltTime = std::time(0);
haltTime = timeCB();
else
baseTime += std::time(0) - haltTime;
baseTime += timeCB() - haltTime;
}
}
void Rtc::setDl(const unsigned new_lowdays) {
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : timeCB();
const std::time_t old_lowdays = ((unixtime - baseTime) / 86400) & 0xFF;
baseTime += old_lowdays * 86400;
baseTime -= new_lowdays * 86400;
}
void Rtc::setH(const unsigned new_hours) {
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : timeCB();
const std::time_t old_hours = ((unixtime - baseTime) / 3600) % 24;
baseTime += old_hours * 3600;
baseTime -= new_hours * 3600;
}
void Rtc::setM(const unsigned new_minutes) {
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : timeCB();
const std::time_t old_minutes = ((unixtime - baseTime) / 60) % 60;
baseTime += old_minutes * 60;
baseTime -= new_minutes * 60;
}
void Rtc::setS(const unsigned new_seconds) {
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : timeCB();
baseTime += (unixtime - baseTime) % 60;
baseTime -= new_seconds;
}

View File

@ -39,6 +39,7 @@ private:
unsigned char dataS;
bool enabled;
bool lastLatchData;
std::time_t (*timeCB)();
void doLatch();
void doSwapActive();
@ -84,6 +85,10 @@ public:
(this->*activeSet)(data);
*activeData = data;
}
void setRTCCallback(std::time_t (*callback)()) {
timeCB = callback;
}
};
}

View File

@ -166,6 +166,10 @@ public:
display.setScanlineCallback(callback, sl);
}
void setRTCCallback(std::time_t (*callback)()) {
cart.setRTCCallback(callback);
}
void setEndtime(unsigned long cc, unsigned long inc);
void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); }

View File

@ -19,6 +19,8 @@
#ifndef SAVESTATE_H
#define SAVESTATE_H
#include <ctime>
namespace gambatte {
class SaverList;
@ -36,7 +38,7 @@ struct SaveState {
void set(T *ptr, const unsigned long sz) { this->ptr = ptr; this->sz = sz; }
friend class SaverList;
friend void setInitState(SaveState &, bool, bool);
friend void setInitState(SaveState &, bool, bool, std::time_t);
};
struct CPU {