bsnes/higan/ws/cartridge/rtc.cpp

151 lines
3.5 KiB
C++

//calculate time between last play of game and current time;
//increment RTC by said amount of seconds
auto Cartridge::rtcLoad() -> void {
uint64 timestamp = 0;
for(auto n : range(8)) timestamp.byte(n) = rtc.data[8 + n];
if(!timestamp) return; //new save file
timestamp = time(0) - timestamp;
while(timestamp--) rtcTickSecond();
}
//save time when game is unloaded
auto Cartridge::rtcSave() -> void {
uint64 timestamp = time(0);
for(auto n : range(8)) rtc.data[8 + n] = timestamp.byte(n);
}
auto Cartridge::rtcTickSecond() -> void {
if(++rtc.second() < 60) return;
rtc.second() = 0;
if(++rtc.minute() < 60) return;
rtc.minute() = 0;
if(++rtc.hour() < 60) return;
rtc.hour() = 0;
rtc.weekday() += 1;
rtc.weekday() %= 7;
uint daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if(rtc.year() && (rtc.year() % 100) && !(rtc.year() % 4)) daysInMonth[1]++;
if(++rtc.day() < daysInMonth[rtc.month()]) return;
rtc.day() = 0;
if(++rtc.month() < 12) return;
rtc.month() = 0;
++rtc.year();
}
auto Cartridge::rtcCheckAlarm() -> void {
if(!rtc.alarm.bit(5)) return;
if(rtc.hour() == rtc.alarmHour && rtc.minute() == rtc.alarmMinute) {
cpu.raise(CPU::Interrupt::Cartridge);
} else {
cpu.lower(CPU::Interrupt::Cartridge);
}
}
auto Cartridge::rtcStatus() -> uint8 {
return 0x80;
}
auto Cartridge::rtcCommand(uint8 data) -> void {
rtc.command = data;
//RESET
if(rtc.command == 0x10) {
rtc.year() = 0;
rtc.month() = 0;
rtc.day() = 0;
rtc.weekday() = 0;
rtc.hour() = 0;
rtc.minute() = 0;
rtc.second() = 0;
}
//ALARM_FLAG
if(rtc.command == 0x12) {
rtc.index = 0;
}
//SET_DATETIME
if(rtc.command == 0x14) {
rtc.index = 0;
}
//GET_DATETIME
if(rtc.command == 0x15) {
rtc.index = 0;
}
//SET_ALARM
if(rtc.command == 0x18) {
rtc.index = 0;
}
}
auto Cartridge::rtcRead() -> uint8 {
uint8 data = 0;
static auto encode = [](uint8 data) -> uint8 {
return ((data / 10) << 4) + (data % 10);
};
//GET_DATETIME
if(rtc.command == 0x15) {
switch(rtc.index) {
case 0: data = encode(rtc.year()); break;
case 1: data = encode(rtc.month() + 1); break;
case 2: data = encode(rtc.day() + 1); break;
case 3: data = encode(rtc.weekday()); break;
case 4: data = encode(rtc.hour()); break;
case 5: data = encode(rtc.minute()); break;
case 6: data = encode(rtc.second()); break;
}
if(++rtc.index >= 7) rtc.command = 0;
}
return data;
}
auto Cartridge::rtcWrite(uint8 data) -> void {
static auto decode = [](uint8 data) -> uint8 {
return (data >> 4) * 10 + (data & 0x0f);
};
//ALARM_FLAG
if(rtc.command == 0x12) {
if(data.bit(6)) rtc.alarm = data; //todo: is bit6 really required to be set?
rtc.command = 0;
rtcCheckAlarm();
}
//SET_DATETIME
if(rtc.command == 0x14) {
switch(rtc.index) {
case 0: rtc.year() = decode(data); break;
case 1: rtc.month() = decode(data) - 1; break;
case 2: rtc.day() = decode(data) - 1; break;
case 3: rtc.weekday() = decode(data); break;
case 4: rtc.hour() = decode(data); break;
case 5: rtc.minute() = decode(data); break;
case 6: rtc.second() = decode(data); break;
}
if(++rtc.index >= 7) rtc.command = 0;
}
//SET_ALRM
if(rtc.command == 0x18) {
switch(rtc.index) {
case 0: rtc.alarmHour = decode(data.bits(0,6)); break;
case 1: rtc.alarmMinute = decode(data); break;
}
if(++rtc.index >= 2) rtc.command = 0;
}
}