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" - " \n" - " \n" - " \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); }