diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp
index b3dd3c11..8348d070 100755
--- a/bsnes/emulator/emulator.hpp
+++ b/bsnes/emulator/emulator.hpp
@@ -3,7 +3,7 @@
namespace Emulator {
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 License[] = "GPLv3";
}
diff --git a/bsnes/emulator/interface.hpp b/bsnes/emulator/interface.hpp
index ae880e87..1d609db1 100755
--- a/bsnes/emulator/interface.hpp
+++ b/bsnes/emulator/interface.hpp
@@ -90,6 +90,10 @@ struct Interface {
virtual void reset() {}
virtual void run() {}
+ //time functions
+ virtual bool rtc() { return false; }
+ virtual void rtcsync() {}
+
//state functions
virtual serializer serialize() = 0;
virtual bool unserialize(serializer&) = 0;
diff --git a/bsnes/nall/snes/cartridge.hpp b/bsnes/nall/snes/cartridge.hpp
index 3577841e..4059df92 100755
--- a/bsnes/nall/snes/cartridge.hpp
+++ b/bsnes/nall/snes/cartridge.hpp
@@ -163,14 +163,6 @@ SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size)
" \n"
" \n"
" \n"
- );
- if(has_spc7110rtc) markup.append(
- " \n"
- );
- markup.append(
" \n"
" \n"
" \n"
@@ -189,6 +181,12 @@ SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size)
" \n"
" \n"
);
+ if(has_spc7110rtc) markup.append(
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ );
}
else if(mapper == LoROM) {
@@ -394,10 +392,10 @@ SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size)
);
if(has_srtc) markup.append(
- " \n"
+ " \n"
" \n"
" \n"
- " \n"
+ " \n"
);
if(has_sdd1) markup.append(
diff --git a/bsnes/sfc/chip/epsonrtc/epsonrtc.cpp b/bsnes/sfc/chip/epsonrtc/epsonrtc.cpp
index 5bfd6e8f..b42e3d83 100755
--- a/bsnes/sfc/chip/epsonrtc/epsonrtc.cpp
+++ b/bsnes/sfc/chip/epsonrtc/epsonrtc.cpp
@@ -21,8 +21,9 @@ void EpsonRTC::enter() {
if(wait) { if(--wait == 0) ready = true; }
clocks++;
- if((clocks & ~255) == 0) duty(); //1/128th second
- if((clocks & ~511) == 0) irq(0); //1/ 64th second
+ if((clocks & ~0x03fff) == 0) duty(); //1/128th second (frequency / 128 - 1)
+ 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
seconds++;
irq(1);
@@ -42,7 +43,45 @@ void EpsonRTC::init() {
void EpsonRTC::load() {
if(cartridge.has_epsonrtc()) interface->memory.append({ID::EpsonRTC, "rtc.ram"});
+
+ secondlo = 0;
+ secondhi = 0;
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() {
@@ -52,7 +91,7 @@ void EpsonRTC::power() {
}
void EpsonRTC::reset() {
- create(EpsonRTC::Enter, 32768);
+ create(EpsonRTC::Enter, 32768 * 64);
clocks = 0;
seconds = 0;
@@ -62,6 +101,52 @@ void EpsonRTC::reset() {
offset = 0;
wait = 0;
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) {
@@ -78,7 +163,7 @@ uint8 EpsonRTC::read(unsigned addr) {
if(state == State::Write) return mdr;
if(state != State::Read) return 0;
ready = false;
- wait = 1;
+ wait = 8;
return rtc_read(offset++);
}
@@ -105,7 +190,7 @@ void EpsonRTC::write(unsigned addr, uint8 data) {
if(data != 0x03 && data != 0x0c) return;
state = State::Seek;
ready = false;
- wait = 1;
+ wait = 8;
mdr = data;
}
@@ -115,14 +200,14 @@ void EpsonRTC::write(unsigned addr, uint8 data) {
offset = data;
ready = false;
- wait = 1;
+ wait = 8;
mdr = data;
}
else if(state == State::Write) {
rtc_write(offset++, data);
ready = false;
- wait = 1;
+ wait = 8;
mdr = data;
}
}
diff --git a/bsnes/sfc/chip/epsonrtc/epsonrtc.hpp b/bsnes/sfc/chip/epsonrtc/epsonrtc.hpp
index 004f14c7..717ad5e2 100755
--- a/bsnes/sfc/chip/epsonrtc/epsonrtc.hpp
+++ b/bsnes/sfc/chip/epsonrtc/epsonrtc.hpp
@@ -9,13 +9,14 @@ struct EpsonRTC : Coprocessor {
void unload();
void power();
void reset();
+ void sync();
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
void serialize(serializer&);
- uint15 clocks;
+ uint21 clocks;
unsigned seconds;
uint2 chipselect;
@@ -24,6 +25,7 @@ struct EpsonRTC : Coprocessor {
uint4 offset;
unsigned wait;
bool ready;
+ uint1 holdtick;
uint4 secondlo;
uint3 secondhi;
@@ -31,28 +33,24 @@ struct EpsonRTC : Coprocessor {
uint4 minutelo;
uint3 minutehi;
- uint1 minutecarry;
+ uint1 resync;
uint4 hourlo;
uint2 hourhi;
uint1 meridian;
- uint1 hourcarry;
uint4 daylo;
uint2 dayhi;
uint1 dayram;
- uint1 daycarry;
uint4 monthlo;
uint1 monthhi;
uint2 monthram;
- uint1 monthcarry;
uint4 yearlo;
uint4 yearhi;
uint3 weekday;
- uint1 weekdaycarry;
uint1 hold;
uint1 calendar;
diff --git a/bsnes/sfc/chip/epsonrtc/memory.cpp b/bsnes/sfc/chip/epsonrtc/memory.cpp
index b3a169ea..f3976227 100755
--- a/bsnes/sfc/chip/epsonrtc/memory.cpp
+++ b/bsnes/sfc/chip/epsonrtc/memory.cpp
@@ -4,9 +4,9 @@ void EpsonRTC::rtc_reset() {
state = State::Mode;
offset = 0;
+ resync = 0;
pause = 0;
test = 0;
- minutecarry = 0;
}
uint4 EpsonRTC::rtc_read(uint4 addr) {
@@ -14,16 +14,16 @@ uint4 EpsonRTC::rtc_read(uint4 addr) {
case 0: return secondlo;
case 1: return secondhi | batteryfailure << 3;
case 2: return minutelo;
- case 3: return minutehi | minutecarry << 3;
+ case 3: return minutehi | resync << 3;
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 7: return dayhi | dayram << 2 | daycarry << 3;
+ case 7: return dayhi | dayram << 2 | resync << 3;
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 11: return yearhi;
- case 12: return weekday | weekdaycarry << 3;
+ case 12: return weekday | resync << 3;
case 13: {
uint1 readflag = irqflag & !irqmask;
irqflag = 0;
@@ -48,7 +48,6 @@ void EpsonRTC::rtc_write(uint4 addr, uint4 data) {
break;
case 3:
minutehi = data;
- minutecarry = data >> 3;
break;
case 4:
hourlo = data;
@@ -56,7 +55,6 @@ void EpsonRTC::rtc_write(uint4 addr, uint4 data) {
case 5:
hourhi = data;
meridian = data >> 2;
- hourcarry = data >> 3;
if(atime == 1) meridian = 0;
if(atime == 0) hourhi &= 1;
break;
@@ -66,7 +64,6 @@ void EpsonRTC::rtc_write(uint4 addr, uint4 data) {
case 7:
dayhi = data;
dayram = data >> 2;
- daycarry = data >> 3;
break;
case 8:
monthlo = data;
@@ -74,7 +71,6 @@ void EpsonRTC::rtc_write(uint4 addr, uint4 data) {
case 9:
monthhi = data;
monthram = data >> 1;
- monthcarry = data >> 3;
break;
case 10:
yearlo = data;
@@ -84,20 +80,25 @@ void EpsonRTC::rtc_write(uint4 addr, uint4 data) {
break;
case 12:
weekday = data;
- weekdaycarry = data >> 3;
break;
- case 13:
+ case 13: {
+ bool held = hold;
hold = data;
calendar = data >> 1;
//irqflag cannot be set manually
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) {
roundseconds = 0;
if(secondhi >= 3) tick_minute();
secondlo = 0;
secondhi = 0;
}
- break;
+ } break;
case 14:
irqmask = data;
irqduty = data >> 1;
@@ -119,10 +120,44 @@ void EpsonRTC::rtc_write(uint4 addr, uint4 data) {
}
void EpsonRTC::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);
- }
+ secondlo = data[0] >> 0;
+ secondhi = data[0] >> 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;
for(unsigned byte = 0; byte < 8; byte++) {
@@ -137,10 +172,14 @@ void EpsonRTC::load(const uint8 *data) {
}
void EpsonRTC::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;
- }
+ data[0] = secondlo << 0 | secondhi << 4 | batteryfailure << 7;
+ data[1] = minutelo << 0 | minutehi << 4 | resync << 7;
+ 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);
for(unsigned byte = 0; byte < 8; byte++) {
diff --git a/bsnes/sfc/chip/epsonrtc/serialization.cpp b/bsnes/sfc/chip/epsonrtc/serialization.cpp
index 6586fcbf..6396fd2d 100755
--- a/bsnes/sfc/chip/epsonrtc/serialization.cpp
+++ b/bsnes/sfc/chip/epsonrtc/serialization.cpp
@@ -12,6 +12,7 @@ void EpsonRTC::serialize(serializer &s) {
s.integer(offset);
s.integer(wait);
s.integer(ready);
+ s.integer(holdtick);
s.integer(secondlo);
s.integer(secondhi);
@@ -19,28 +20,24 @@ void EpsonRTC::serialize(serializer &s) {
s.integer(minutelo);
s.integer(minutehi);
- s.integer(minutecarry);
+ s.integer(resync);
s.integer(hourlo);
s.integer(hourhi);
s.integer(meridian);
- s.integer(hourcarry);
s.integer(daylo);
s.integer(dayhi);
s.integer(dayram);
- s.integer(daycarry);
s.integer(monthlo);
s.integer(monthhi);
s.integer(monthram);
- s.integer(monthcarry);
s.integer(yearlo);
s.integer(yearhi);
s.integer(weekday);
- s.integer(weekdaycarry);
s.integer(hold);
s.integer(calendar);
diff --git a/bsnes/sfc/chip/epsonrtc/time.cpp b/bsnes/sfc/chip/epsonrtc/time.cpp
index 456238c7..b9cfcd3f 100755
--- a/bsnes/sfc/chip/epsonrtc/time.cpp
+++ b/bsnes/sfc/chip/epsonrtc/time.cpp
@@ -1,6 +1,9 @@
#ifdef EPSONRTC_CPP
void EpsonRTC::irq(uint2 period) {
+ if(stop) return;
+ if(pause) return;
+
if(period == irqperiod) irqflag = 1;
}
@@ -9,14 +12,20 @@ void EpsonRTC::duty() {
}
void EpsonRTC::tick() {
- if(hold) return;
- if(pause) return;
if(stop) return;
+ if(pause) return;
- minutecarry = true;
+ if(hold) {
+ holdtick = true;
+ return;
+ }
+
+ resync = true;
tick_second();
}
+//below code provides bit-perfect emulation of invalid BCD values on the RTC-4513
+
void EpsonRTC::tick_second() {
if(secondlo <= 8 || secondlo == 12) {
secondlo++;
diff --git a/bsnes/sfc/chip/sharprtc/memory.cpp b/bsnes/sfc/chip/sharprtc/memory.cpp
new file mode 100755
index 00000000..2813a782
--- /dev/null
+++ b/bsnes/sfc/chip/sharprtc/memory.cpp
@@ -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;
+ }
+}
diff --git a/bsnes/sfc/chip/sharprtc/serialization.cpp b/bsnes/sfc/chip/sharprtc/serialization.cpp
index 5d63fc94..f4f62a68 100755
--- a/bsnes/sfc/chip/sharprtc/serialization.cpp
+++ b/bsnes/sfc/chip/sharprtc/serialization.cpp
@@ -1,9 +1,16 @@
#ifdef SHARPRTC_CPP
void SharpRTC::serialize(serializer &s) {
- s.array(rtc);
s.integer(rtc_mode);
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
diff --git a/bsnes/sfc/chip/sharprtc/sharprtc.cpp b/bsnes/sfc/chip/sharprtc/sharprtc.cpp
index 8977d1a3..022bff1b 100755
--- a/bsnes/sfc/chip/sharprtc/sharprtc.cpp
+++ b/bsnes/sfc/chip/sharprtc/sharprtc.cpp
@@ -3,18 +3,41 @@
#define SHARPRTC_CPP
namespace SuperFamicom {
+#include "memory.cpp"
+#include "time.cpp"
+#include "serialization.cpp"
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::load() {
- for(unsigned n = 0; n < 20; n++) rtc[n] = 0xff;
interface->memory.append({ID::SharpRTC, "rtc.ram"});
+
+ second = 0;
+ minute = 0;
+ hour = 0;
+ day = 0;
+ month = 0;
+ year = 0;
+ weekday = 0;
}
void SharpRTC::unload() {
@@ -24,135 +47,23 @@ void SharpRTC::power() {
}
void SharpRTC::reset() {
+ create(SharpRTC::Enter, 1);
+
rtc_mode = RtcRead;
rtc_index = -1;
- update_time();
}
-void SharpRTC::update_time() {
- time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24);
- time_t current_time = time(0);
+void SharpRTC::sync() {
+ time_t systime = time(0);
+ tm *timeinfo = localtime(&systime);
- //sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic.
- //yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
- //accounting for overflow at the cost of 1-bit precision (to catch underflow). this will allow
- //rtc[] timestamp to remain valid for up to ~34 years from the last update, even if
- //time_t overflows. calculation should be valid regardless of number representation, time_t size,
- //or whether time_t is signed or unsigned.
- time_t diff
- = (current_time >= rtc_time)
- ? (current_time - rtc_time)
- : (std::numeric_limits::max() - rtc_time + current_time + 1); //compensate for overflow
- if(diff > std::numeric_limits::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
+ second = min(59, timeinfo->tm_sec); //round leap second down
+ minute = timeinfo->tm_min;
+ hour = timeinfo->tm_hour;
+ day = timeinfo->tm_mday;
+ month = 1 + timeinfo->tm_mon;
+ year = 900 + timeinfo->tm_year;
+ weekday = timeinfo->tm_wday;
}
uint8 SharpRTC::read(unsigned addr) {
@@ -162,14 +73,13 @@ uint8 SharpRTC::read(unsigned addr) {
if(rtc_mode != RtcRead) return 0x00;
if(rtc_index < 0) {
- update_time();
rtc_index++;
return 0x0f;
} else if(rtc_index > 12) {
rtc_index = -1;
return 0x0f;
} 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_index >= 0 && rtc_index < 12) {
- rtc[rtc_index++] = data;
+ rtc_write(rtc_index++, data);
if(rtc_index == 12) {
//day of week is automatically calculated and written
- unsigned day = rtc[ 6] + rtc[ 7] * 10;
- unsigned month = rtc[ 8];
- unsigned year = rtc[ 9] + rtc[10] * 10 + rtc[11] * 100;
- year += 1000;
-
- rtc[rtc_index++] = weekday(year, month, day);
+ weekday = calculate_weekday(1000 + year, month, day);
}
}
} else if(rtc_mode == RtcCommand) {
@@ -216,7 +121,15 @@ void SharpRTC::write(unsigned addr, uint8 data) {
} else if(data == 4) {
rtc_mode = RtcReady;
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 {
//unknown behavior
rtc_mode = RtcReady;
diff --git a/bsnes/sfc/chip/sharprtc/sharprtc.hpp b/bsnes/sfc/chip/sharprtc/sharprtc.hpp
index 57b190b6..0749671c 100755
--- a/bsnes/sfc/chip/sharprtc/sharprtc.hpp
+++ b/bsnes/sfc/chip/sharprtc/sharprtc.hpp
@@ -1,25 +1,48 @@
-struct SharpRTC {
- uint8 rtc[20];
+struct SharpRTC : Coprocessor {
+ static void Enter();
+ void enter();
void init();
void load();
void unload();
void power();
void reset();
+ void sync();
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
void serialize(serializer&);
-private:
- static const unsigned months[12];
enum RtcMode { RtcReady, RtcCommand, RtcRead, RtcWrite };
unsigned rtc_mode;
signed rtc_index;
- void update_time();
- unsigned weekday(unsigned year, unsigned month, unsigned day);
+ unsigned second;
+ 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;
diff --git a/bsnes/sfc/chip/sharprtc/time.cpp b/bsnes/sfc/chip/sharprtc/time.cpp
new file mode 100755
index 00000000..27b9d8b5
--- /dev/null
+++ b/bsnes/sfc/chip/sharprtc/time.cpp
@@ -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
+}
diff --git a/bsnes/sfc/interface/interface.cpp b/bsnes/sfc/interface/interface.cpp
index 91715ca8..58bc8dbc 100755
--- a/bsnes/sfc/interface/interface.cpp
+++ b/bsnes/sfc/interface/interface.cpp
@@ -112,7 +112,9 @@ void Interface::load(unsigned id, const stream &stream, const string &markup) {
}
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) {
@@ -146,13 +148,15 @@ void Interface::save(unsigned id, const stream &stream) {
}
if(id == ID::EpsonRTC) {
- uint8 data[16];
+ uint8 data[16] = {0};
epsonrtc.save(data);
stream.write(data, sizeof data);
}
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) {
@@ -197,6 +201,17 @@ void Interface::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() {
system.runtosave();
return system.serialize();
diff --git a/bsnes/sfc/interface/interface.hpp b/bsnes/sfc/interface/interface.hpp
index db2577e9..7af7aa8c 100755
--- a/bsnes/sfc/interface/interface.hpp
+++ b/bsnes/sfc/interface/interface.hpp
@@ -48,6 +48,9 @@ struct Interface : Emulator::Interface {
void reset();
void run();
+ bool rtc();
+ void rtcsync();
+
serializer serialize();
bool unserialize(serializer&);
diff --git a/bsnes/sfc/system/system.cpp b/bsnes/sfc/system/system.cpp
index 98e04f89..f661f0d6 100755
--- a/bsnes/sfc/system/system.cpp
+++ b/bsnes/sfc/system/system.cpp
@@ -214,6 +214,7 @@ void System::reset() {
if(cartridge.has_hitachidsp()) cpu.coprocessors.append(&hitachidsp);
if(cartridge.has_necdsp()) cpu.coprocessors.append(&necdsp);
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_msu1()) cpu.coprocessors.append(&msu1);
if(cartridge.has_link()) cpu.coprocessors.append(&link);
diff --git a/bsnes/target-ethos/general/presentation.cpp b/bsnes/target-ethos/general/presentation.cpp
index 5daa4840..7aca3dd7 100755
--- a/bsnes/target-ethos/general/presentation.cpp
+++ b/bsnes/target-ethos/general/presentation.cpp
@@ -29,6 +29,7 @@ void Presentation::synchronize() {
synchronizeAudio.setChecked(config->audio.synchronize);
muteAudio.setChecked(config->audio.mute);
toolsMenu.setVisible(application->active);
+ synchronizeTime.setVisible(application->active && system().rtc());
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});
loadStateMenu.setText("Load State");
for(unsigned n = 0; n < 5; n++) loadStateItem[n].setText({"Slot ", 1 + n});
+ synchronizeTime.setText("Synchronize Time");
resizeWindow.setText("Resize Window");
cheatEditor.setText("Cheat Editor");
stateManager.setText("State Manager");
@@ -95,6 +97,7 @@ Presentation::Presentation() : active(nullptr) {
for(unsigned n = 0; n < 5; n++) saveStateMenu.append(saveStateItem[n]);
toolsMenu.append(loadStateMenu);
for(unsigned n = 0; n < 5; n++) loadStateMenu.append(loadStateItem[n]);
+ toolsMenu.append(synchronizeTime);
toolsMenu.append(stateMenuSeparator);
toolsMenu.append(resizeWindow, cheatEditor, stateManager);
@@ -117,6 +120,7 @@ Presentation::Presentation() : active(nullptr) {
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++) loadStateItem[n].onActivate = [=] { utility->loadState(1 + n); };
+ synchronizeTime.onActivate = [&] { if(application->active) system().rtcsync(); };
resizeWindow.onActivate = [&] { utility->resize(true); };
cheatEditor.onActivate = [&] { ::cheatEditor->setVisible(); };
stateManager.onActivate = [&] { ::stateManager->setVisible(); };
diff --git a/bsnes/target-ethos/general/presentation.hpp b/bsnes/target-ethos/general/presentation.hpp
index 622a40a3..597083a3 100755
--- a/bsnes/target-ethos/general/presentation.hpp
+++ b/bsnes/target-ethos/general/presentation.hpp
@@ -43,6 +43,7 @@ struct Presentation : Window {
Item saveStateItem[5];
Menu loadStateMenu;
Item loadStateItem[5];
+ Item synchronizeTime;
Separator stateMenuSeparator;
Item resizeWindow;
Item cheatEditor;
diff --git a/bsnes/target-ethos/utility/utility.cpp b/bsnes/target-ethos/utility/utility.cpp
index 9346a6d4..83e72d04 100755
--- a/bsnes/target-ethos/utility/utility.cpp
+++ b/bsnes/target-ethos/utility/utility.cpp
@@ -53,6 +53,7 @@ void Utility::loadMedia(unsigned id, const string &name, const string &type, con
void Utility::loadMemory() {
for(auto &memory : system().memory) {
string pathname = path(system().group(memory.id));
+ if(file::exists({pathname, memory.name}) == false) continue;
filestream fs({pathname, memory.name});
system().load(memory.id, fs);
}