mirror of https://github.com/bsnes-emu/bsnes.git
232 lines
6.0 KiB
C++
Executable File
232 lines
6.0 KiB
C++
Executable File
#include <snes.hpp>
|
|
|
|
#define SRTC_CPP
|
|
namespace SNES {
|
|
|
|
SRTC srtc;
|
|
|
|
#include "serialization.cpp"
|
|
|
|
const unsigned SRTC::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
|
|
|
void SRTC::init() {
|
|
}
|
|
|
|
void SRTC::enable() {
|
|
}
|
|
|
|
void SRTC::power() {
|
|
reset();
|
|
}
|
|
|
|
void SRTC::reset() {
|
|
rtc_mode = RtcRead;
|
|
rtc_index = -1;
|
|
update_time();
|
|
}
|
|
|
|
void SRTC::update_time() {
|
|
time_t rtc_time
|
|
= (memory::cartrtc.read(16) << 0)
|
|
| (memory::cartrtc.read(17) << 8)
|
|
| (memory::cartrtc.read(18) << 16)
|
|
| (memory::cartrtc.read(19) << 24);
|
|
time_t current_time = time(0);
|
|
|
|
//sizeof(time_t) is platform-dependent; though memory::cartrtc 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
|
|
//memory::cartrtc 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<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 = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10;
|
|
unsigned minute = memory::cartrtc.read( 2) + memory::cartrtc.read( 3) * 10;
|
|
unsigned hour = memory::cartrtc.read( 4) + memory::cartrtc.read( 5) * 10;
|
|
unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10;
|
|
unsigned month = memory::cartrtc.read( 8);
|
|
unsigned year = memory::cartrtc.read( 9) + memory::cartrtc.read(10) * 10 + memory::cartrtc.read(11) * 100;
|
|
unsigned weekday = memory::cartrtc.read(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;
|
|
|
|
memory::cartrtc.write( 0, second % 10);
|
|
memory::cartrtc.write( 1, second / 10);
|
|
memory::cartrtc.write( 2, minute % 10);
|
|
memory::cartrtc.write( 3, minute / 10);
|
|
memory::cartrtc.write( 4, hour % 10);
|
|
memory::cartrtc.write( 5, hour / 10);
|
|
memory::cartrtc.write( 6, day % 10);
|
|
memory::cartrtc.write( 7, day / 10);
|
|
memory::cartrtc.write( 8, month);
|
|
memory::cartrtc.write( 9, year % 10);
|
|
memory::cartrtc.write(10, (year / 10) % 10);
|
|
memory::cartrtc.write(11, year / 100);
|
|
memory::cartrtc.write(12, weekday % 7);
|
|
}
|
|
|
|
memory::cartrtc.write(16, current_time >> 0);
|
|
memory::cartrtc.write(17, current_time >> 8);
|
|
memory::cartrtc.write(18, current_time >> 16);
|
|
memory::cartrtc.write(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 SRTC::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 SRTC::mmio_read(unsigned addr) {
|
|
addr &= 0xffff;
|
|
|
|
if(addr == 0x2800) {
|
|
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 memory::cartrtc.read(rtc_index++);
|
|
}
|
|
}
|
|
|
|
return cpu.regs.mdr;
|
|
}
|
|
|
|
void SRTC::mmio_write(unsigned addr, uint8 data) {
|
|
addr &= 0xffff;
|
|
|
|
if(addr == 0x2801) {
|
|
data &= 0x0f; //only the low four bits are used
|
|
|
|
if(data == 0x0d) {
|
|
rtc_mode = RtcRead;
|
|
rtc_index = -1;
|
|
return;
|
|
}
|
|
|
|
if(data == 0x0e) {
|
|
rtc_mode = RtcCommand;
|
|
return;
|
|
}
|
|
|
|
if(data == 0x0f) return; //unknown behavior
|
|
|
|
if(rtc_mode == RtcWrite) {
|
|
if(rtc_index >= 0 && rtc_index < 12) {
|
|
memory::cartrtc.write(rtc_index++, data);
|
|
|
|
if(rtc_index == 12) {
|
|
//day of week is automatically calculated and written
|
|
unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10;
|
|
unsigned month = memory::cartrtc.read( 8);
|
|
unsigned year = memory::cartrtc.read( 9) + memory::cartrtc.read(10) * 10 + memory::cartrtc.read(11) * 100;
|
|
year += 1000;
|
|
|
|
memory::cartrtc.write(rtc_index++, weekday(year, month, day));
|
|
}
|
|
}
|
|
} else if(rtc_mode == RtcCommand) {
|
|
if(data == 0) {
|
|
rtc_mode = RtcWrite;
|
|
rtc_index = 0;
|
|
} else if(data == 4) {
|
|
rtc_mode = RtcReady;
|
|
rtc_index = -1;
|
|
for(unsigned i = 0; i < 13; i++) memory::cartrtc.write(i, 0);
|
|
} else {
|
|
//unknown behavior
|
|
rtc_mode = RtcReady;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SRTC::SRTC() {
|
|
}
|
|
|
|
}
|