mirror of https://github.com/bsnes-emu/bsnes.git
Update to v089r07 release.
byuu says: Changelog: - EpsonRTC emulation improved further (stop/pause blocks IRQs, verified secondhi >= 3 triggers 30-second adjust (even on invalid BCD), second-changed flag is mirrored to minute+hour+day+month+weekday, improved busy timing, etc.) - SharpRTC rewritten, works like EpsonRTC now in that it has its own timing thread and ticks with the emulation - won't attempt to read from an unopen file stream now (I think this is what was crashing Sufami Turbo without SRAM?) - added Tools -> Synchronize Time option below load/save state options. Only appears when you play a game with an emulated RTC chip Just realized that I used 125ms for the 30-second adjust instead of 125us, so I'll fix that in the next WIP. Aside from that, this is as good as the emulation is going to get. There's still a couple of absolutely psychopathic edge cases that are just too damn difficult to simulate. So that leaves us with data port control + decompression status registers to investigate before SPC7110 will be finished.
This commit is contained in:
parent
d6001a2df4
commit
5dbd5f4d0f
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const char Name[] = "bsnes";
|
static const char Name[] = "bsnes";
|
||||||
static const char Version[] = "089.06";
|
static const char Version[] = "089.07";
|
||||||
static const char Author[] = "byuu";
|
static const char Author[] = "byuu";
|
||||||
static const char License[] = "GPLv3";
|
static const char License[] = "GPLv3";
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,10 @@ struct Interface {
|
||||||
virtual void reset() {}
|
virtual void reset() {}
|
||||||
virtual void run() {}
|
virtual void run() {}
|
||||||
|
|
||||||
|
//time functions
|
||||||
|
virtual bool rtc() { return false; }
|
||||||
|
virtual void rtcsync() {}
|
||||||
|
|
||||||
//state functions
|
//state functions
|
||||||
virtual serializer serialize() = 0;
|
virtual serializer serialize() = 0;
|
||||||
virtual bool unserialize(serializer&) = 0;
|
virtual bool unserialize(serializer&) = 0;
|
||||||
|
|
|
@ -163,14 +163,6 @@ SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size)
|
||||||
" <map address='00-3f:4800-483f'/>\n"
|
" <map address='00-3f:4800-483f'/>\n"
|
||||||
" <map address='80-bf:4800-483f'/>\n"
|
" <map address='80-bf:4800-483f'/>\n"
|
||||||
" </mmio>\n"
|
" </mmio>\n"
|
||||||
);
|
|
||||||
if(has_spc7110rtc) markup.append(
|
|
||||||
" <rtc>\n"
|
|
||||||
" <map address='00-3f:4840-4842'/>\n"
|
|
||||||
" <map address='80-bf:4840-4842'/>\n"
|
|
||||||
" </rtc>\n"
|
|
||||||
);
|
|
||||||
markup.append(
|
|
||||||
" <dcu>\n"
|
" <dcu>\n"
|
||||||
" <map address='50:0000-ffff'/>\n"
|
" <map address='50:0000-ffff'/>\n"
|
||||||
" </dcu>\n"
|
" </dcu>\n"
|
||||||
|
@ -189,6 +181,12 @@ SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size)
|
||||||
" </mcu>\n"
|
" </mcu>\n"
|
||||||
" </spc7110>\n"
|
" </spc7110>\n"
|
||||||
);
|
);
|
||||||
|
if(has_spc7110rtc) markup.append(
|
||||||
|
" <epsonrtc>\n"
|
||||||
|
" <map address='00-3f:4840-4842'/>\n"
|
||||||
|
" <map address='80-bf:4840-4842'/>\n"
|
||||||
|
" </epsonrtc>\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(mapper == LoROM) {
|
else if(mapper == LoROM) {
|
||||||
|
@ -394,10 +392,10 @@ SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size)
|
||||||
);
|
);
|
||||||
|
|
||||||
if(has_srtc) markup.append(
|
if(has_srtc) markup.append(
|
||||||
" <srtc>\n"
|
" <sharprtc>\n"
|
||||||
" <map address='00-3f:2800-2801'/>\n"
|
" <map address='00-3f:2800-2801'/>\n"
|
||||||
" <map address='80-bf:2800-2801'/>\n"
|
" <map address='80-bf:2800-2801'/>\n"
|
||||||
" </srtc>\n"
|
" </sharprtc>\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
if(has_sdd1) markup.append(
|
if(has_sdd1) markup.append(
|
||||||
|
|
|
@ -21,8 +21,9 @@ void EpsonRTC::enter() {
|
||||||
if(wait) { if(--wait == 0) ready = true; }
|
if(wait) { if(--wait == 0) ready = true; }
|
||||||
|
|
||||||
clocks++;
|
clocks++;
|
||||||
if((clocks & ~255) == 0) duty(); //1/128th second
|
if((clocks & ~0x03fff) == 0) duty(); //1/128th second (frequency / 128 - 1)
|
||||||
if((clocks & ~511) == 0) irq(0); //1/ 64th second
|
if((clocks & ~0x07fff) == 0) irq(0); //1/ 64th second (frequency / 64 - 1)
|
||||||
|
if((clocks & ~0x3ffff) == 0) roundseconds = 0; //1/ 8th second (frequency / 8 - 1)
|
||||||
if(clocks == 0) { //1 second
|
if(clocks == 0) { //1 second
|
||||||
seconds++;
|
seconds++;
|
||||||
irq(1);
|
irq(1);
|
||||||
|
@ -42,7 +43,45 @@ void EpsonRTC::init() {
|
||||||
|
|
||||||
void EpsonRTC::load() {
|
void EpsonRTC::load() {
|
||||||
if(cartridge.has_epsonrtc()) interface->memory.append({ID::EpsonRTC, "rtc.ram"});
|
if(cartridge.has_epsonrtc()) interface->memory.append({ID::EpsonRTC, "rtc.ram"});
|
||||||
|
|
||||||
|
secondlo = 0;
|
||||||
|
secondhi = 0;
|
||||||
batteryfailure = 1;
|
batteryfailure = 1;
|
||||||
|
|
||||||
|
minutelo = 0;
|
||||||
|
minutehi = 0;
|
||||||
|
resync = 0;
|
||||||
|
|
||||||
|
hourlo = 0;
|
||||||
|
hourhi = 0;
|
||||||
|
meridian = 0;
|
||||||
|
|
||||||
|
daylo = 0;
|
||||||
|
dayhi = 0;
|
||||||
|
dayram = 0;
|
||||||
|
|
||||||
|
monthlo = 0;
|
||||||
|
monthhi = 0;
|
||||||
|
monthram = 0;
|
||||||
|
|
||||||
|
yearlo = 0;
|
||||||
|
yearhi = 0;
|
||||||
|
|
||||||
|
weekday = 0;
|
||||||
|
|
||||||
|
hold = 0;
|
||||||
|
calendar = 0;
|
||||||
|
irqflag = 0;
|
||||||
|
roundseconds = 0;
|
||||||
|
|
||||||
|
irqmask = 0;
|
||||||
|
irqduty = 0;
|
||||||
|
irqperiod = 0;
|
||||||
|
|
||||||
|
pause = 0;
|
||||||
|
stop = 0;
|
||||||
|
atime = 0;
|
||||||
|
test = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpsonRTC::unload() {
|
void EpsonRTC::unload() {
|
||||||
|
@ -52,7 +91,7 @@ void EpsonRTC::power() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpsonRTC::reset() {
|
void EpsonRTC::reset() {
|
||||||
create(EpsonRTC::Enter, 32768);
|
create(EpsonRTC::Enter, 32768 * 64);
|
||||||
|
|
||||||
clocks = 0;
|
clocks = 0;
|
||||||
seconds = 0;
|
seconds = 0;
|
||||||
|
@ -62,6 +101,52 @@ void EpsonRTC::reset() {
|
||||||
offset = 0;
|
offset = 0;
|
||||||
wait = 0;
|
wait = 0;
|
||||||
ready = false;
|
ready = false;
|
||||||
|
holdtick = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpsonRTC::sync() {
|
||||||
|
time_t systime = time(0);
|
||||||
|
tm *timeinfo = localtime(&systime);
|
||||||
|
|
||||||
|
unsigned second = min(59, timeinfo->tm_sec); //round down leap second
|
||||||
|
secondlo = second % 10;
|
||||||
|
secondhi = second / 10;
|
||||||
|
|
||||||
|
unsigned minute = timeinfo->tm_min;
|
||||||
|
minutelo = minute % 10;
|
||||||
|
minutehi = minute / 10;
|
||||||
|
|
||||||
|
unsigned hour = timeinfo->tm_hour;
|
||||||
|
if(atime) {
|
||||||
|
hourlo = hour % 10;
|
||||||
|
hourhi = hour / 10;
|
||||||
|
} else {
|
||||||
|
meridian = hour >= 12;
|
||||||
|
hour %= 12;
|
||||||
|
if(hour == 0) {
|
||||||
|
hourlo = 2;
|
||||||
|
hourhi = 1;
|
||||||
|
} else {
|
||||||
|
hourlo = hour % 10;
|
||||||
|
hourhi = hour / 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned day = timeinfo->tm_mday;
|
||||||
|
daylo = day % 10;
|
||||||
|
dayhi = day / 10;
|
||||||
|
|
||||||
|
unsigned month = 1 + timeinfo->tm_mon;
|
||||||
|
monthlo = month % 10;
|
||||||
|
monthhi = month / 10;
|
||||||
|
|
||||||
|
unsigned year = timeinfo->tm_year % 100;
|
||||||
|
yearlo = year % 10;
|
||||||
|
yearhi = year / 10;
|
||||||
|
|
||||||
|
weekday = timeinfo->tm_wday;
|
||||||
|
|
||||||
|
resync = true; //alert RTC that time has changed
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 EpsonRTC::read(unsigned addr) {
|
uint8 EpsonRTC::read(unsigned addr) {
|
||||||
|
@ -78,7 +163,7 @@ uint8 EpsonRTC::read(unsigned addr) {
|
||||||
if(state == State::Write) return mdr;
|
if(state == State::Write) return mdr;
|
||||||
if(state != State::Read) return 0;
|
if(state != State::Read) return 0;
|
||||||
ready = false;
|
ready = false;
|
||||||
wait = 1;
|
wait = 8;
|
||||||
return rtc_read(offset++);
|
return rtc_read(offset++);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +190,7 @@ void EpsonRTC::write(unsigned addr, uint8 data) {
|
||||||
if(data != 0x03 && data != 0x0c) return;
|
if(data != 0x03 && data != 0x0c) return;
|
||||||
state = State::Seek;
|
state = State::Seek;
|
||||||
ready = false;
|
ready = false;
|
||||||
wait = 1;
|
wait = 8;
|
||||||
mdr = data;
|
mdr = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,14 +200,14 @@ void EpsonRTC::write(unsigned addr, uint8 data) {
|
||||||
|
|
||||||
offset = data;
|
offset = data;
|
||||||
ready = false;
|
ready = false;
|
||||||
wait = 1;
|
wait = 8;
|
||||||
mdr = data;
|
mdr = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(state == State::Write) {
|
else if(state == State::Write) {
|
||||||
rtc_write(offset++, data);
|
rtc_write(offset++, data);
|
||||||
ready = false;
|
ready = false;
|
||||||
wait = 1;
|
wait = 8;
|
||||||
mdr = data;
|
mdr = data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,14 @@ struct EpsonRTC : Coprocessor {
|
||||||
void unload();
|
void unload();
|
||||||
void power();
|
void power();
|
||||||
void reset();
|
void reset();
|
||||||
|
void sync();
|
||||||
|
|
||||||
uint8 read(unsigned addr);
|
uint8 read(unsigned addr);
|
||||||
void write(unsigned addr, uint8 data);
|
void write(unsigned addr, uint8 data);
|
||||||
|
|
||||||
void serialize(serializer&);
|
void serialize(serializer&);
|
||||||
|
|
||||||
uint15 clocks;
|
uint21 clocks;
|
||||||
unsigned seconds;
|
unsigned seconds;
|
||||||
|
|
||||||
uint2 chipselect;
|
uint2 chipselect;
|
||||||
|
@ -24,6 +25,7 @@ struct EpsonRTC : Coprocessor {
|
||||||
uint4 offset;
|
uint4 offset;
|
||||||
unsigned wait;
|
unsigned wait;
|
||||||
bool ready;
|
bool ready;
|
||||||
|
uint1 holdtick;
|
||||||
|
|
||||||
uint4 secondlo;
|
uint4 secondlo;
|
||||||
uint3 secondhi;
|
uint3 secondhi;
|
||||||
|
@ -31,28 +33,24 @@ struct EpsonRTC : Coprocessor {
|
||||||
|
|
||||||
uint4 minutelo;
|
uint4 minutelo;
|
||||||
uint3 minutehi;
|
uint3 minutehi;
|
||||||
uint1 minutecarry;
|
uint1 resync;
|
||||||
|
|
||||||
uint4 hourlo;
|
uint4 hourlo;
|
||||||
uint2 hourhi;
|
uint2 hourhi;
|
||||||
uint1 meridian;
|
uint1 meridian;
|
||||||
uint1 hourcarry;
|
|
||||||
|
|
||||||
uint4 daylo;
|
uint4 daylo;
|
||||||
uint2 dayhi;
|
uint2 dayhi;
|
||||||
uint1 dayram;
|
uint1 dayram;
|
||||||
uint1 daycarry;
|
|
||||||
|
|
||||||
uint4 monthlo;
|
uint4 monthlo;
|
||||||
uint1 monthhi;
|
uint1 monthhi;
|
||||||
uint2 monthram;
|
uint2 monthram;
|
||||||
uint1 monthcarry;
|
|
||||||
|
|
||||||
uint4 yearlo;
|
uint4 yearlo;
|
||||||
uint4 yearhi;
|
uint4 yearhi;
|
||||||
|
|
||||||
uint3 weekday;
|
uint3 weekday;
|
||||||
uint1 weekdaycarry;
|
|
||||||
|
|
||||||
uint1 hold;
|
uint1 hold;
|
||||||
uint1 calendar;
|
uint1 calendar;
|
||||||
|
|
|
@ -4,9 +4,9 @@ void EpsonRTC::rtc_reset() {
|
||||||
state = State::Mode;
|
state = State::Mode;
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
|
||||||
|
resync = 0;
|
||||||
pause = 0;
|
pause = 0;
|
||||||
test = 0;
|
test = 0;
|
||||||
minutecarry = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint4 EpsonRTC::rtc_read(uint4 addr) {
|
uint4 EpsonRTC::rtc_read(uint4 addr) {
|
||||||
|
@ -14,16 +14,16 @@ uint4 EpsonRTC::rtc_read(uint4 addr) {
|
||||||
case 0: return secondlo;
|
case 0: return secondlo;
|
||||||
case 1: return secondhi | batteryfailure << 3;
|
case 1: return secondhi | batteryfailure << 3;
|
||||||
case 2: return minutelo;
|
case 2: return minutelo;
|
||||||
case 3: return minutehi | minutecarry << 3;
|
case 3: return minutehi | resync << 3;
|
||||||
case 4: return hourlo;
|
case 4: return hourlo;
|
||||||
case 5: return hourhi | meridian << 2 | hourcarry << 3;
|
case 5: return hourhi | meridian << 2 | resync << 3;
|
||||||
case 6: return daylo;
|
case 6: return daylo;
|
||||||
case 7: return dayhi | dayram << 2 | daycarry << 3;
|
case 7: return dayhi | dayram << 2 | resync << 3;
|
||||||
case 8: return monthlo;
|
case 8: return monthlo;
|
||||||
case 9: return monthhi | monthram << 1 | monthcarry << 3;
|
case 9: return monthhi | monthram << 1 | resync << 3;
|
||||||
case 10: return yearlo;
|
case 10: return yearlo;
|
||||||
case 11: return yearhi;
|
case 11: return yearhi;
|
||||||
case 12: return weekday | weekdaycarry << 3;
|
case 12: return weekday | resync << 3;
|
||||||
case 13: {
|
case 13: {
|
||||||
uint1 readflag = irqflag & !irqmask;
|
uint1 readflag = irqflag & !irqmask;
|
||||||
irqflag = 0;
|
irqflag = 0;
|
||||||
|
@ -48,7 +48,6 @@ void EpsonRTC::rtc_write(uint4 addr, uint4 data) {
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
minutehi = data;
|
minutehi = data;
|
||||||
minutecarry = data >> 3;
|
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
hourlo = data;
|
hourlo = data;
|
||||||
|
@ -56,7 +55,6 @@ void EpsonRTC::rtc_write(uint4 addr, uint4 data) {
|
||||||
case 5:
|
case 5:
|
||||||
hourhi = data;
|
hourhi = data;
|
||||||
meridian = data >> 2;
|
meridian = data >> 2;
|
||||||
hourcarry = data >> 3;
|
|
||||||
if(atime == 1) meridian = 0;
|
if(atime == 1) meridian = 0;
|
||||||
if(atime == 0) hourhi &= 1;
|
if(atime == 0) hourhi &= 1;
|
||||||
break;
|
break;
|
||||||
|
@ -66,7 +64,6 @@ void EpsonRTC::rtc_write(uint4 addr, uint4 data) {
|
||||||
case 7:
|
case 7:
|
||||||
dayhi = data;
|
dayhi = data;
|
||||||
dayram = data >> 2;
|
dayram = data >> 2;
|
||||||
daycarry = data >> 3;
|
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
monthlo = data;
|
monthlo = data;
|
||||||
|
@ -74,7 +71,6 @@ void EpsonRTC::rtc_write(uint4 addr, uint4 data) {
|
||||||
case 9:
|
case 9:
|
||||||
monthhi = data;
|
monthhi = data;
|
||||||
monthram = data >> 1;
|
monthram = data >> 1;
|
||||||
monthcarry = data >> 3;
|
|
||||||
break;
|
break;
|
||||||
case 10:
|
case 10:
|
||||||
yearlo = data;
|
yearlo = data;
|
||||||
|
@ -84,20 +80,25 @@ void EpsonRTC::rtc_write(uint4 addr, uint4 data) {
|
||||||
break;
|
break;
|
||||||
case 12:
|
case 12:
|
||||||
weekday = data;
|
weekday = data;
|
||||||
weekdaycarry = data >> 3;
|
|
||||||
break;
|
break;
|
||||||
case 13:
|
case 13: {
|
||||||
|
bool held = hold;
|
||||||
hold = data;
|
hold = data;
|
||||||
calendar = data >> 1;
|
calendar = data >> 1;
|
||||||
//irqflag cannot be set manually
|
//irqflag cannot be set manually
|
||||||
roundseconds = data >> 3;
|
roundseconds = data >> 3;
|
||||||
|
if(held == 1 && hold == 0 && holdtick) {
|
||||||
|
//if a second has passed during hold; increment one second upon resuming
|
||||||
|
holdtick = false;
|
||||||
|
tick_second();
|
||||||
|
}
|
||||||
if(roundseconds) {
|
if(roundseconds) {
|
||||||
roundseconds = 0;
|
roundseconds = 0;
|
||||||
if(secondhi >= 3) tick_minute();
|
if(secondhi >= 3) tick_minute();
|
||||||
secondlo = 0;
|
secondlo = 0;
|
||||||
secondhi = 0;
|
secondhi = 0;
|
||||||
}
|
}
|
||||||
break;
|
} break;
|
||||||
case 14:
|
case 14:
|
||||||
irqmask = data;
|
irqmask = data;
|
||||||
irqduty = data >> 1;
|
irqduty = data >> 1;
|
||||||
|
@ -119,10 +120,44 @@ void EpsonRTC::rtc_write(uint4 addr, uint4 data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpsonRTC::load(const uint8 *data) {
|
void EpsonRTC::load(const uint8 *data) {
|
||||||
for(unsigned byte = 0; byte < 8; byte++) {
|
secondlo = data[0] >> 0;
|
||||||
rtc_write(byte * 2 + 0, data[byte] >> 0);
|
secondhi = data[0] >> 4;
|
||||||
rtc_write(byte * 2 + 1, data[byte] >> 4);
|
batteryfailure = data[0] >> 7;
|
||||||
}
|
|
||||||
|
minutelo = data[1] >> 0;
|
||||||
|
minutehi = data[1] >> 4;
|
||||||
|
resync = data[1] >> 7;
|
||||||
|
|
||||||
|
hourlo = data[2] >> 0;
|
||||||
|
hourhi = data[2] >> 4;
|
||||||
|
meridian = data[2] >> 6;
|
||||||
|
|
||||||
|
daylo = data[3] >> 0;
|
||||||
|
dayhi = data[3] >> 4;
|
||||||
|
dayram = data[3] >> 6;
|
||||||
|
|
||||||
|
monthlo = data[4] >> 0;
|
||||||
|
monthhi = data[4] >> 4;
|
||||||
|
monthram = data[4] >> 5;
|
||||||
|
|
||||||
|
yearlo = data[5] >> 0;
|
||||||
|
yearhi = data[5] >> 4;
|
||||||
|
|
||||||
|
weekday = data[6] >> 0;
|
||||||
|
|
||||||
|
hold = data[6] >> 4;
|
||||||
|
calendar = data[6] >> 5;
|
||||||
|
irqflag = data[6] >> 6;
|
||||||
|
roundseconds = data[6] >> 7;
|
||||||
|
|
||||||
|
irqmask = data[7] >> 0;
|
||||||
|
irqduty = data[7] >> 1;
|
||||||
|
irqperiod = data[7] >> 2;
|
||||||
|
|
||||||
|
pause = data[7] >> 4;
|
||||||
|
stop = data[7] >> 5;
|
||||||
|
atime = data[7] >> 6;
|
||||||
|
test = data[7] >> 7;
|
||||||
|
|
||||||
uint64 timestamp = 0;
|
uint64 timestamp = 0;
|
||||||
for(unsigned byte = 0; byte < 8; byte++) {
|
for(unsigned byte = 0; byte < 8; byte++) {
|
||||||
|
@ -137,10 +172,14 @@ void EpsonRTC::load(const uint8 *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpsonRTC::save(uint8 *data) {
|
void EpsonRTC::save(uint8 *data) {
|
||||||
for(unsigned byte = 0; byte < 8; byte++) {
|
data[0] = secondlo << 0 | secondhi << 4 | batteryfailure << 7;
|
||||||
data[byte] = rtc_read(byte * 2 + 0) << 0;
|
data[1] = minutelo << 0 | minutehi << 4 | resync << 7;
|
||||||
data[byte] |= rtc_read(byte * 2 + 1) << 4;
|
data[2] = hourlo << 0 | hourhi << 4 | meridian << 6;
|
||||||
}
|
data[3] = daylo << 0 | dayhi << 4 | dayram << 6;
|
||||||
|
data[4] = monthlo << 0 | monthhi << 4 | monthram << 5;
|
||||||
|
data[5] = yearlo << 0 | yearhi << 4;
|
||||||
|
data[6] = weekday << 0 | hold << 4 | calendar << 5 | irqflag << 6 | roundseconds << 7;
|
||||||
|
data[7] = irqmask << 0 | irqduty << 1 | irqperiod << 2 | pause << 4 | stop << 5 | atime << 6 | test << 7;
|
||||||
|
|
||||||
uint64 timestamp = (uint64)time(0);
|
uint64 timestamp = (uint64)time(0);
|
||||||
for(unsigned byte = 0; byte < 8; byte++) {
|
for(unsigned byte = 0; byte < 8; byte++) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ void EpsonRTC::serialize(serializer &s) {
|
||||||
s.integer(offset);
|
s.integer(offset);
|
||||||
s.integer(wait);
|
s.integer(wait);
|
||||||
s.integer(ready);
|
s.integer(ready);
|
||||||
|
s.integer(holdtick);
|
||||||
|
|
||||||
s.integer(secondlo);
|
s.integer(secondlo);
|
||||||
s.integer(secondhi);
|
s.integer(secondhi);
|
||||||
|
@ -19,28 +20,24 @@ void EpsonRTC::serialize(serializer &s) {
|
||||||
|
|
||||||
s.integer(minutelo);
|
s.integer(minutelo);
|
||||||
s.integer(minutehi);
|
s.integer(minutehi);
|
||||||
s.integer(minutecarry);
|
s.integer(resync);
|
||||||
|
|
||||||
s.integer(hourlo);
|
s.integer(hourlo);
|
||||||
s.integer(hourhi);
|
s.integer(hourhi);
|
||||||
s.integer(meridian);
|
s.integer(meridian);
|
||||||
s.integer(hourcarry);
|
|
||||||
|
|
||||||
s.integer(daylo);
|
s.integer(daylo);
|
||||||
s.integer(dayhi);
|
s.integer(dayhi);
|
||||||
s.integer(dayram);
|
s.integer(dayram);
|
||||||
s.integer(daycarry);
|
|
||||||
|
|
||||||
s.integer(monthlo);
|
s.integer(monthlo);
|
||||||
s.integer(monthhi);
|
s.integer(monthhi);
|
||||||
s.integer(monthram);
|
s.integer(monthram);
|
||||||
s.integer(monthcarry);
|
|
||||||
|
|
||||||
s.integer(yearlo);
|
s.integer(yearlo);
|
||||||
s.integer(yearhi);
|
s.integer(yearhi);
|
||||||
|
|
||||||
s.integer(weekday);
|
s.integer(weekday);
|
||||||
s.integer(weekdaycarry);
|
|
||||||
|
|
||||||
s.integer(hold);
|
s.integer(hold);
|
||||||
s.integer(calendar);
|
s.integer(calendar);
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#ifdef EPSONRTC_CPP
|
#ifdef EPSONRTC_CPP
|
||||||
|
|
||||||
void EpsonRTC::irq(uint2 period) {
|
void EpsonRTC::irq(uint2 period) {
|
||||||
|
if(stop) return;
|
||||||
|
if(pause) return;
|
||||||
|
|
||||||
if(period == irqperiod) irqflag = 1;
|
if(period == irqperiod) irqflag = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,14 +12,20 @@ void EpsonRTC::duty() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpsonRTC::tick() {
|
void EpsonRTC::tick() {
|
||||||
if(hold) return;
|
|
||||||
if(pause) return;
|
|
||||||
if(stop) return;
|
if(stop) return;
|
||||||
|
if(pause) return;
|
||||||
|
|
||||||
minutecarry = true;
|
if(hold) {
|
||||||
|
holdtick = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resync = true;
|
||||||
tick_second();
|
tick_second();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//below code provides bit-perfect emulation of invalid BCD values on the RTC-4513
|
||||||
|
|
||||||
void EpsonRTC::tick_second() {
|
void EpsonRTC::tick_second() {
|
||||||
if(secondlo <= 8 || secondlo == 12) {
|
if(secondlo <= 8 || secondlo == 12) {
|
||||||
secondlo++;
|
secondlo++;
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
uint4 SharpRTC::rtc_read(uint4 addr) {
|
||||||
|
switch(addr) {
|
||||||
|
case 0: return second % 10;
|
||||||
|
case 1: return second / 10;
|
||||||
|
case 2: return minute % 10;
|
||||||
|
case 3: return minute / 10;
|
||||||
|
case 4: return hour % 10;
|
||||||
|
case 5: return hour / 10;
|
||||||
|
case 6: return day % 10;
|
||||||
|
case 7: return day / 10;
|
||||||
|
case 8: return month;
|
||||||
|
case 9: return year % 10;
|
||||||
|
case 10: return year / 10 % 10;
|
||||||
|
case 11: return year / 100;
|
||||||
|
case 12: return weekday;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharpRTC::rtc_write(uint4 addr, uint4 data) {
|
||||||
|
switch(addr) {
|
||||||
|
case 0: second = second / 10 * 10 + data; break;
|
||||||
|
case 1: second = data * 10 + second % 10; break;
|
||||||
|
case 2: minute = minute / 10 * 10 + data; break;
|
||||||
|
case 3: minute = data * 10 + minute % 10; break;
|
||||||
|
case 4: hour = hour / 10 * 10 + data; break;
|
||||||
|
case 5: hour = data * 10 + hour % 10; break;
|
||||||
|
case 6: day = day / 10 * 10 + data; break;
|
||||||
|
case 7: day = data * 10 + day % 10; break;
|
||||||
|
case 8: month = data; break;
|
||||||
|
case 9: year = year / 10 * 10 + data; break;
|
||||||
|
case 10: year = year / 100 * 100 + data * 10 + year % 10; break;
|
||||||
|
case 11: year = data * 100 + year % 100; break;
|
||||||
|
case 12: weekday = data; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharpRTC::load(const uint8 *data) {
|
||||||
|
for(unsigned byte = 0; byte < 8; byte++) {
|
||||||
|
rtc_write(byte * 2 + 0, data[byte] >> 0);
|
||||||
|
rtc_write(byte * 2 + 1, data[byte] >> 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 timestamp = 0;
|
||||||
|
for(unsigned byte = 0; byte < 8; byte++) {
|
||||||
|
timestamp |= data[8 + byte] << (byte * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 diff = (uint64)time(0) - timestamp;
|
||||||
|
while(diff >= 60 * 60 * 24) { tick_day(); diff -= 60 * 60 * 24; }
|
||||||
|
while(diff >= 60 * 60) { tick_hour(); diff -= 60 * 60; }
|
||||||
|
while(diff >= 60) { tick_minute(); diff -= 60; }
|
||||||
|
while(diff--) tick_second();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharpRTC::save(uint8 *data) {
|
||||||
|
for(unsigned byte = 0; byte < 8; byte++) {
|
||||||
|
data[byte] = rtc_read(byte * 2 + 0) << 0;
|
||||||
|
data[byte] |= rtc_read(byte * 2 + 1) << 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 timestamp = (uint64)time(0);
|
||||||
|
for(unsigned byte = 0; byte < 8; byte++) {
|
||||||
|
data[8 + byte] = timestamp;
|
||||||
|
timestamp >>= 8;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,16 @@
|
||||||
#ifdef SHARPRTC_CPP
|
#ifdef SHARPRTC_CPP
|
||||||
|
|
||||||
void SharpRTC::serialize(serializer &s) {
|
void SharpRTC::serialize(serializer &s) {
|
||||||
s.array(rtc);
|
|
||||||
s.integer(rtc_mode);
|
s.integer(rtc_mode);
|
||||||
s.integer(rtc_index);
|
s.integer(rtc_index);
|
||||||
|
|
||||||
|
s.integer(second);
|
||||||
|
s.integer(minute);
|
||||||
|
s.integer(hour);
|
||||||
|
s.integer(day);
|
||||||
|
s.integer(month);
|
||||||
|
s.integer(year);
|
||||||
|
s.integer(weekday);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,18 +3,41 @@
|
||||||
#define SHARPRTC_CPP
|
#define SHARPRTC_CPP
|
||||||
namespace SuperFamicom {
|
namespace SuperFamicom {
|
||||||
|
|
||||||
|
#include "memory.cpp"
|
||||||
|
#include "time.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
SharpRTC sharprtc;
|
SharpRTC sharprtc;
|
||||||
|
|
||||||
#include "serialization.cpp"
|
void SharpRTC::Enter() {
|
||||||
|
sharprtc.enter();
|
||||||
|
}
|
||||||
|
|
||||||
const unsigned SharpRTC::months[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
void SharpRTC::enter() {
|
||||||
|
while(true) {
|
||||||
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
tick_second();
|
||||||
|
|
||||||
|
step(1);
|
||||||
|
synchronize_cpu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SharpRTC::init() {
|
void SharpRTC::init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharpRTC::load() {
|
void SharpRTC::load() {
|
||||||
for(unsigned n = 0; n < 20; n++) rtc[n] = 0xff;
|
|
||||||
interface->memory.append({ID::SharpRTC, "rtc.ram"});
|
interface->memory.append({ID::SharpRTC, "rtc.ram"});
|
||||||
|
|
||||||
|
second = 0;
|
||||||
|
minute = 0;
|
||||||
|
hour = 0;
|
||||||
|
day = 0;
|
||||||
|
month = 0;
|
||||||
|
year = 0;
|
||||||
|
weekday = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharpRTC::unload() {
|
void SharpRTC::unload() {
|
||||||
|
@ -24,135 +47,23 @@ void SharpRTC::power() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharpRTC::reset() {
|
void SharpRTC::reset() {
|
||||||
|
create(SharpRTC::Enter, 1);
|
||||||
|
|
||||||
rtc_mode = RtcRead;
|
rtc_mode = RtcRead;
|
||||||
rtc_index = -1;
|
rtc_index = -1;
|
||||||
update_time();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharpRTC::update_time() {
|
void SharpRTC::sync() {
|
||||||
time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24);
|
time_t systime = time(0);
|
||||||
time_t current_time = time(0);
|
tm *timeinfo = localtime(&systime);
|
||||||
|
|
||||||
//sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic.
|
second = min(59, timeinfo->tm_sec); //round leap second down
|
||||||
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
|
minute = timeinfo->tm_min;
|
||||||
//accounting for overflow at the cost of 1-bit precision (to catch underflow). this will allow
|
hour = timeinfo->tm_hour;
|
||||||
//rtc[] timestamp to remain valid for up to ~34 years from the last update, even if
|
day = timeinfo->tm_mday;
|
||||||
//time_t overflows. calculation should be valid regardless of number representation, time_t size,
|
month = 1 + timeinfo->tm_mon;
|
||||||
//or whether time_t is signed or unsigned.
|
year = 900 + timeinfo->tm_year;
|
||||||
time_t diff
|
weekday = timeinfo->tm_wday;
|
||||||
= (current_time >= rtc_time)
|
|
||||||
? (current_time - rtc_time)
|
|
||||||
: (std::numeric_limits<time_t>::max() - rtc_time + current_time + 1); //compensate for overflow
|
|
||||||
if(diff > std::numeric_limits<time_t>::max() / 2) diff = 0; //compensate for underflow
|
|
||||||
|
|
||||||
if(diff > 0) {
|
|
||||||
unsigned second = rtc[ 0] + rtc[ 1] * 10;
|
|
||||||
unsigned minute = rtc[ 2] + rtc[ 3] * 10;
|
|
||||||
unsigned hour = rtc[ 4] + rtc[ 5] * 10;
|
|
||||||
unsigned day = rtc[ 6] + rtc[ 7] * 10;
|
|
||||||
unsigned month = rtc[ 8];
|
|
||||||
unsigned year = rtc[ 9] + rtc[10] * 10 + rtc[11] * 100;
|
|
||||||
unsigned weekday = rtc[12];
|
|
||||||
|
|
||||||
day--;
|
|
||||||
month--;
|
|
||||||
year += 1000;
|
|
||||||
|
|
||||||
second += diff;
|
|
||||||
while(second >= 60) {
|
|
||||||
second -= 60;
|
|
||||||
|
|
||||||
minute++;
|
|
||||||
if(minute < 60) continue;
|
|
||||||
minute = 0;
|
|
||||||
|
|
||||||
hour++;
|
|
||||||
if(hour < 24) continue;
|
|
||||||
hour = 0;
|
|
||||||
|
|
||||||
day++;
|
|
||||||
weekday = (weekday + 1) % 7;
|
|
||||||
unsigned days = months[month % 12];
|
|
||||||
if(days == 28) {
|
|
||||||
bool leapyear = false;
|
|
||||||
if((year % 4) == 0) {
|
|
||||||
leapyear = true;
|
|
||||||
if((year % 100) == 0 && (year % 400) != 0) leapyear = false;
|
|
||||||
}
|
|
||||||
if(leapyear) days++;
|
|
||||||
}
|
|
||||||
if(day < days) continue;
|
|
||||||
day = 0;
|
|
||||||
|
|
||||||
month++;
|
|
||||||
if(month < 12) continue;
|
|
||||||
month = 0;
|
|
||||||
|
|
||||||
year++;
|
|
||||||
}
|
|
||||||
|
|
||||||
day++;
|
|
||||||
month++;
|
|
||||||
year -= 1000;
|
|
||||||
|
|
||||||
rtc[ 0] = second % 10;
|
|
||||||
rtc[ 1] = second / 10;
|
|
||||||
rtc[ 2] = minute % 10;
|
|
||||||
rtc[ 3] = minute / 10;
|
|
||||||
rtc[ 4] = hour % 10;
|
|
||||||
rtc[ 5] = hour / 10;
|
|
||||||
rtc[ 6] = day % 10;
|
|
||||||
rtc[ 7] = day / 10;
|
|
||||||
rtc[ 8] = month;
|
|
||||||
rtc[ 9] = year % 10;
|
|
||||||
rtc[10] = (year / 10) % 10;
|
|
||||||
rtc[11] = year / 100;
|
|
||||||
rtc[12] = weekday % 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
rtc[16] = current_time >> 0;
|
|
||||||
rtc[17] = current_time >> 8;
|
|
||||||
rtc[18] = current_time >> 16;
|
|
||||||
rtc[19] = current_time >> 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
//returns day of week for specified date
|
|
||||||
//eg 0 = Sunday, 1 = Monday, ... 6 = Saturday
|
|
||||||
//usage: weekday(2008, 1, 1) returns weekday of January 1st, 2008
|
|
||||||
unsigned SharpRTC::weekday(unsigned year, unsigned month, unsigned day) {
|
|
||||||
unsigned y = 1900, m = 1; //epoch is 1900-01-01
|
|
||||||
unsigned sum = 0; //number of days passed since epoch
|
|
||||||
|
|
||||||
year = max(1900, year);
|
|
||||||
month = max(1, min(12, month));
|
|
||||||
day = max(1, min(31, day));
|
|
||||||
|
|
||||||
while(y < year) {
|
|
||||||
bool leapyear = false;
|
|
||||||
if((y % 4) == 0) {
|
|
||||||
leapyear = true;
|
|
||||||
if((y % 100) == 0 && (y % 400) != 0) leapyear = false;
|
|
||||||
}
|
|
||||||
sum += leapyear ? 366 : 365;
|
|
||||||
y++;
|
|
||||||
}
|
|
||||||
|
|
||||||
while(m < month) {
|
|
||||||
unsigned days = months[m - 1];
|
|
||||||
if(days == 28) {
|
|
||||||
bool leapyear = false;
|
|
||||||
if((y % 4) == 0) {
|
|
||||||
leapyear = true;
|
|
||||||
if((y % 100) == 0 && (y % 400) != 0) leapyear = false;
|
|
||||||
}
|
|
||||||
if(leapyear) days++;
|
|
||||||
}
|
|
||||||
sum += days;
|
|
||||||
m++;
|
|
||||||
}
|
|
||||||
|
|
||||||
sum += day - 1;
|
|
||||||
return (sum + 1) % 7; //1900-01-01 was a Monday
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 SharpRTC::read(unsigned addr) {
|
uint8 SharpRTC::read(unsigned addr) {
|
||||||
|
@ -162,14 +73,13 @@ uint8 SharpRTC::read(unsigned addr) {
|
||||||
if(rtc_mode != RtcRead) return 0x00;
|
if(rtc_mode != RtcRead) return 0x00;
|
||||||
|
|
||||||
if(rtc_index < 0) {
|
if(rtc_index < 0) {
|
||||||
update_time();
|
|
||||||
rtc_index++;
|
rtc_index++;
|
||||||
return 0x0f;
|
return 0x0f;
|
||||||
} else if(rtc_index > 12) {
|
} else if(rtc_index > 12) {
|
||||||
rtc_index = -1;
|
rtc_index = -1;
|
||||||
return 0x0f;
|
return 0x0f;
|
||||||
} else {
|
} else {
|
||||||
return rtc[rtc_index++];
|
return rtc_read(rtc_index++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,16 +107,11 @@ void SharpRTC::write(unsigned addr, uint8 data) {
|
||||||
|
|
||||||
if(rtc_mode == RtcWrite) {
|
if(rtc_mode == RtcWrite) {
|
||||||
if(rtc_index >= 0 && rtc_index < 12) {
|
if(rtc_index >= 0 && rtc_index < 12) {
|
||||||
rtc[rtc_index++] = data;
|
rtc_write(rtc_index++, data);
|
||||||
|
|
||||||
if(rtc_index == 12) {
|
if(rtc_index == 12) {
|
||||||
//day of week is automatically calculated and written
|
//day of week is automatically calculated and written
|
||||||
unsigned day = rtc[ 6] + rtc[ 7] * 10;
|
weekday = calculate_weekday(1000 + year, month, day);
|
||||||
unsigned month = rtc[ 8];
|
|
||||||
unsigned year = rtc[ 9] + rtc[10] * 10 + rtc[11] * 100;
|
|
||||||
year += 1000;
|
|
||||||
|
|
||||||
rtc[rtc_index++] = weekday(year, month, day);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(rtc_mode == RtcCommand) {
|
} else if(rtc_mode == RtcCommand) {
|
||||||
|
@ -216,7 +121,15 @@ void SharpRTC::write(unsigned addr, uint8 data) {
|
||||||
} else if(data == 4) {
|
} else if(data == 4) {
|
||||||
rtc_mode = RtcReady;
|
rtc_mode = RtcReady;
|
||||||
rtc_index = -1;
|
rtc_index = -1;
|
||||||
for(unsigned i = 0; i < 13; i++) rtc[i] = 0;
|
|
||||||
|
//reset time
|
||||||
|
second = 0;
|
||||||
|
minute = 0;
|
||||||
|
hour = 0;
|
||||||
|
day = 0;
|
||||||
|
month = 0;
|
||||||
|
year = 0;
|
||||||
|
weekday = 0;
|
||||||
} else {
|
} else {
|
||||||
//unknown behavior
|
//unknown behavior
|
||||||
rtc_mode = RtcReady;
|
rtc_mode = RtcReady;
|
||||||
|
|
|
@ -1,25 +1,48 @@
|
||||||
struct SharpRTC {
|
struct SharpRTC : Coprocessor {
|
||||||
uint8 rtc[20];
|
static void Enter();
|
||||||
|
void enter();
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void load();
|
void load();
|
||||||
void unload();
|
void unload();
|
||||||
void power();
|
void power();
|
||||||
void reset();
|
void reset();
|
||||||
|
void sync();
|
||||||
|
|
||||||
uint8 read(unsigned addr);
|
uint8 read(unsigned addr);
|
||||||
void write(unsigned addr, uint8 data);
|
void write(unsigned addr, uint8 data);
|
||||||
|
|
||||||
void serialize(serializer&);
|
void serialize(serializer&);
|
||||||
|
|
||||||
private:
|
|
||||||
static const unsigned months[12];
|
|
||||||
enum RtcMode { RtcReady, RtcCommand, RtcRead, RtcWrite };
|
enum RtcMode { RtcReady, RtcCommand, RtcRead, RtcWrite };
|
||||||
unsigned rtc_mode;
|
unsigned rtc_mode;
|
||||||
signed rtc_index;
|
signed rtc_index;
|
||||||
|
|
||||||
void update_time();
|
unsigned second;
|
||||||
unsigned weekday(unsigned year, unsigned month, unsigned day);
|
unsigned minute;
|
||||||
|
unsigned hour;
|
||||||
|
unsigned day;
|
||||||
|
unsigned month;
|
||||||
|
unsigned year;
|
||||||
|
unsigned weekday;
|
||||||
|
|
||||||
|
//memory.cpp
|
||||||
|
uint4 rtc_read(uint4 addr);
|
||||||
|
void rtc_write(uint4 addr, uint4 data);
|
||||||
|
|
||||||
|
void load(const uint8 *data);
|
||||||
|
void save(uint8 *data);
|
||||||
|
|
||||||
|
//time.cpp
|
||||||
|
static const unsigned daysinmonth[12];
|
||||||
|
void tick_second();
|
||||||
|
void tick_minute();
|
||||||
|
void tick_hour();
|
||||||
|
void tick_day();
|
||||||
|
void tick_month();
|
||||||
|
void tick_year();
|
||||||
|
|
||||||
|
unsigned calculate_weekday(unsigned year, unsigned month, unsigned day);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern SharpRTC sharprtc;
|
extern SharpRTC sharprtc;
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
const unsigned SharpRTC::daysinmonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||||
|
|
||||||
|
void SharpRTC::tick_second() {
|
||||||
|
if(++second < 60) return;
|
||||||
|
second = 0;
|
||||||
|
tick_minute();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharpRTC::tick_minute() {
|
||||||
|
if(++minute < 60) return;
|
||||||
|
minute = 0;
|
||||||
|
tick_hour();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharpRTC::tick_hour() {
|
||||||
|
if(++hour < 24) return;
|
||||||
|
hour = 0;
|
||||||
|
tick_day();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharpRTC::tick_day() {
|
||||||
|
unsigned days = daysinmonth[month % 12];
|
||||||
|
if(day++ < days) return;
|
||||||
|
day = 0;
|
||||||
|
tick_month();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharpRTC::tick_month() {
|
||||||
|
if(month++ < 12) return;
|
||||||
|
month = 0;
|
||||||
|
tick_year();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharpRTC::tick_year() {
|
||||||
|
year++;
|
||||||
|
year = (uint12)year;
|
||||||
|
}
|
||||||
|
|
||||||
|
//returns day of week for specified date
|
||||||
|
//eg 0 = Sunday, 1 = Monday, ... 6 = Saturday
|
||||||
|
//usage: weekday(2008, 1, 1) returns weekday of January 1st, 2008
|
||||||
|
unsigned SharpRTC::calculate_weekday(unsigned year, unsigned month, unsigned day) {
|
||||||
|
unsigned y = 1900, m = 1; //epoch is 1900-01-01
|
||||||
|
unsigned sum = 0; //number of days passed since epoch
|
||||||
|
|
||||||
|
year = max(1900, year);
|
||||||
|
month = max(1, min(12, month));
|
||||||
|
day = max(1, min(31, day));
|
||||||
|
|
||||||
|
while(y < year) {
|
||||||
|
bool leapyear = false;
|
||||||
|
if((y % 4) == 0) {
|
||||||
|
leapyear = true;
|
||||||
|
if((y % 100) == 0 && (y % 400) != 0) leapyear = false;
|
||||||
|
}
|
||||||
|
sum += leapyear ? 366 : 365;
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(m < month) {
|
||||||
|
unsigned days = daysinmonth[m - 1];
|
||||||
|
if(days == 28) {
|
||||||
|
bool leapyear = false;
|
||||||
|
if((y % 4) == 0) {
|
||||||
|
leapyear = true;
|
||||||
|
if((y % 100) == 0 && (y % 400) != 0) leapyear = false;
|
||||||
|
}
|
||||||
|
if(leapyear) days++;
|
||||||
|
}
|
||||||
|
sum += days;
|
||||||
|
m++;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum += day - 1;
|
||||||
|
return (sum + 1) % 7; //1900-01-01 was a Monday
|
||||||
|
}
|
|
@ -112,7 +112,9 @@ void Interface::load(unsigned id, const stream &stream, const string &markup) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(id == ID::SharpRTC) {
|
if(id == ID::SharpRTC) {
|
||||||
stream.read(sharprtc.rtc, min(stream.size(), sizeof sharprtc.rtc));
|
uint8 data[16] = {0};
|
||||||
|
stream.read(data, min(stream.size(), sizeof data));
|
||||||
|
sharprtc.load(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(id == ID::BsxRAM) {
|
if(id == ID::BsxRAM) {
|
||||||
|
@ -146,13 +148,15 @@ void Interface::save(unsigned id, const stream &stream) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(id == ID::EpsonRTC) {
|
if(id == ID::EpsonRTC) {
|
||||||
uint8 data[16];
|
uint8 data[16] = {0};
|
||||||
epsonrtc.save(data);
|
epsonrtc.save(data);
|
||||||
stream.write(data, sizeof data);
|
stream.write(data, sizeof data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(id == ID::SharpRTC) {
|
if(id == ID::SharpRTC) {
|
||||||
stream.write(sharprtc.rtc, sizeof sharprtc.rtc);
|
uint8 data[16] = {0};
|
||||||
|
sharprtc.save(data);
|
||||||
|
stream.write(data, sizeof data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(id == ID::BsxRAM) {
|
if(id == ID::BsxRAM) {
|
||||||
|
@ -197,6 +201,17 @@ void Interface::run() {
|
||||||
system.run();
|
system.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Interface::rtc() {
|
||||||
|
if(cartridge.has_epsonrtc()) return true;
|
||||||
|
if(cartridge.has_sharprtc()) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface::rtcsync() {
|
||||||
|
if(cartridge.has_epsonrtc()) epsonrtc.sync();
|
||||||
|
if(cartridge.has_sharprtc()) sharprtc.sync();
|
||||||
|
}
|
||||||
|
|
||||||
serializer Interface::serialize() {
|
serializer Interface::serialize() {
|
||||||
system.runtosave();
|
system.runtosave();
|
||||||
return system.serialize();
|
return system.serialize();
|
||||||
|
|
|
@ -48,6 +48,9 @@ struct Interface : Emulator::Interface {
|
||||||
void reset();
|
void reset();
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
bool rtc();
|
||||||
|
void rtcsync();
|
||||||
|
|
||||||
serializer serialize();
|
serializer serialize();
|
||||||
bool unserialize(serializer&);
|
bool unserialize(serializer&);
|
||||||
|
|
||||||
|
|
|
@ -214,6 +214,7 @@ void System::reset() {
|
||||||
if(cartridge.has_hitachidsp()) cpu.coprocessors.append(&hitachidsp);
|
if(cartridge.has_hitachidsp()) cpu.coprocessors.append(&hitachidsp);
|
||||||
if(cartridge.has_necdsp()) cpu.coprocessors.append(&necdsp);
|
if(cartridge.has_necdsp()) cpu.coprocessors.append(&necdsp);
|
||||||
if(cartridge.has_epsonrtc()) cpu.coprocessors.append(&epsonrtc);
|
if(cartridge.has_epsonrtc()) cpu.coprocessors.append(&epsonrtc);
|
||||||
|
if(cartridge.has_sharprtc()) cpu.coprocessors.append(&sharprtc);
|
||||||
if(cartridge.has_spc7110()) cpu.coprocessors.append(&spc7110);
|
if(cartridge.has_spc7110()) cpu.coprocessors.append(&spc7110);
|
||||||
if(cartridge.has_msu1()) cpu.coprocessors.append(&msu1);
|
if(cartridge.has_msu1()) cpu.coprocessors.append(&msu1);
|
||||||
if(cartridge.has_link()) cpu.coprocessors.append(&link);
|
if(cartridge.has_link()) cpu.coprocessors.append(&link);
|
||||||
|
|
|
@ -29,6 +29,7 @@ void Presentation::synchronize() {
|
||||||
synchronizeAudio.setChecked(config->audio.synchronize);
|
synchronizeAudio.setChecked(config->audio.synchronize);
|
||||||
muteAudio.setChecked(config->audio.mute);
|
muteAudio.setChecked(config->audio.mute);
|
||||||
toolsMenu.setVisible(application->active);
|
toolsMenu.setVisible(application->active);
|
||||||
|
synchronizeTime.setVisible(application->active && system().rtc());
|
||||||
resizeWindow.setVisible(config->video.scaleMode != 2);
|
resizeWindow.setVisible(config->video.scaleMode != 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +69,7 @@ Presentation::Presentation() : active(nullptr) {
|
||||||
for(unsigned n = 0; n < 5; n++) saveStateItem[n].setText({"Slot ", 1 + n});
|
for(unsigned n = 0; n < 5; n++) saveStateItem[n].setText({"Slot ", 1 + n});
|
||||||
loadStateMenu.setText("Load State");
|
loadStateMenu.setText("Load State");
|
||||||
for(unsigned n = 0; n < 5; n++) loadStateItem[n].setText({"Slot ", 1 + n});
|
for(unsigned n = 0; n < 5; n++) loadStateItem[n].setText({"Slot ", 1 + n});
|
||||||
|
synchronizeTime.setText("Synchronize Time");
|
||||||
resizeWindow.setText("Resize Window");
|
resizeWindow.setText("Resize Window");
|
||||||
cheatEditor.setText("Cheat Editor");
|
cheatEditor.setText("Cheat Editor");
|
||||||
stateManager.setText("State Manager");
|
stateManager.setText("State Manager");
|
||||||
|
@ -95,6 +97,7 @@ Presentation::Presentation() : active(nullptr) {
|
||||||
for(unsigned n = 0; n < 5; n++) saveStateMenu.append(saveStateItem[n]);
|
for(unsigned n = 0; n < 5; n++) saveStateMenu.append(saveStateItem[n]);
|
||||||
toolsMenu.append(loadStateMenu);
|
toolsMenu.append(loadStateMenu);
|
||||||
for(unsigned n = 0; n < 5; n++) loadStateMenu.append(loadStateItem[n]);
|
for(unsigned n = 0; n < 5; n++) loadStateMenu.append(loadStateItem[n]);
|
||||||
|
toolsMenu.append(synchronizeTime);
|
||||||
toolsMenu.append(stateMenuSeparator);
|
toolsMenu.append(stateMenuSeparator);
|
||||||
toolsMenu.append(resizeWindow, cheatEditor, stateManager);
|
toolsMenu.append(resizeWindow, cheatEditor, stateManager);
|
||||||
|
|
||||||
|
@ -117,6 +120,7 @@ Presentation::Presentation() : active(nullptr) {
|
||||||
configurationSettings.onActivate = [&] { settings->setVisible(); settings->panelList.setFocused(); };
|
configurationSettings.onActivate = [&] { settings->setVisible(); settings->panelList.setFocused(); };
|
||||||
for(unsigned n = 0; n < 5; n++) saveStateItem[n].onActivate = [=] { utility->saveState(1 + n); };
|
for(unsigned n = 0; n < 5; n++) saveStateItem[n].onActivate = [=] { utility->saveState(1 + n); };
|
||||||
for(unsigned n = 0; n < 5; n++) loadStateItem[n].onActivate = [=] { utility->loadState(1 + n); };
|
for(unsigned n = 0; n < 5; n++) loadStateItem[n].onActivate = [=] { utility->loadState(1 + n); };
|
||||||
|
synchronizeTime.onActivate = [&] { if(application->active) system().rtcsync(); };
|
||||||
resizeWindow.onActivate = [&] { utility->resize(true); };
|
resizeWindow.onActivate = [&] { utility->resize(true); };
|
||||||
cheatEditor.onActivate = [&] { ::cheatEditor->setVisible(); };
|
cheatEditor.onActivate = [&] { ::cheatEditor->setVisible(); };
|
||||||
stateManager.onActivate = [&] { ::stateManager->setVisible(); };
|
stateManager.onActivate = [&] { ::stateManager->setVisible(); };
|
||||||
|
|
|
@ -43,6 +43,7 @@ struct Presentation : Window {
|
||||||
Item saveStateItem[5];
|
Item saveStateItem[5];
|
||||||
Menu loadStateMenu;
|
Menu loadStateMenu;
|
||||||
Item loadStateItem[5];
|
Item loadStateItem[5];
|
||||||
|
Item synchronizeTime;
|
||||||
Separator stateMenuSeparator;
|
Separator stateMenuSeparator;
|
||||||
Item resizeWindow;
|
Item resizeWindow;
|
||||||
Item cheatEditor;
|
Item cheatEditor;
|
||||||
|
|
|
@ -53,6 +53,7 @@ void Utility::loadMedia(unsigned id, const string &name, const string &type, con
|
||||||
void Utility::loadMemory() {
|
void Utility::loadMemory() {
|
||||||
for(auto &memory : system().memory) {
|
for(auto &memory : system().memory) {
|
||||||
string pathname = path(system().group(memory.id));
|
string pathname = path(system().group(memory.id));
|
||||||
|
if(file::exists({pathname, memory.name}) == false) continue;
|
||||||
filestream fs({pathname, memory.name});
|
filestream fs({pathname, memory.name});
|
||||||
system().load(memory.id, fs);
|
system().load(memory.id, fs);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue