bsnes/higan/sfc/coprocessor/epsonrtc/time.cpp

183 lines
3.7 KiB
C++

auto EpsonRTC::irq(uint2 period) -> void {
if(stop || pause) return;
if(period == irqperiod) irqflag = 1;
}
auto EpsonRTC::duty() -> void {
if(irqduty) irqflag = 0;
}
auto EpsonRTC::round_seconds() -> void {
if(roundseconds == 0) return;
roundseconds = 0;
if(secondhi >= 3) tick_minute();
secondlo = 0;
secondhi = 0;
}
auto EpsonRTC::tick() -> void {
if(stop || pause) return;
if(hold) {
holdtick = 1;
return;
}
resync = 1;
tick_second();
}
//below code provides bit-perfect emulation of invalid BCD values on the RTC-4513
//code makes extensive use of variable-length integers (see epsonrtc.hpp for sizes)
auto EpsonRTC::tick_second() -> void {
if(secondlo <= 8 || secondlo == 12) {
secondlo++;
} else {
secondlo = 0;
if(secondhi < 5) {
secondhi++;
} else {
secondhi = 0;
tick_minute();
}
}
}
auto EpsonRTC::tick_minute() -> void {
if(minutelo <= 8 || minutelo == 12) {
minutelo++;
} else {
minutelo = 0;
if(minutehi < 5) {
minutehi++;
} else {
minutehi = 0;
tick_hour();
}
}
}
auto EpsonRTC::tick_hour() -> void {
if(atime) {
if(hourhi < 2) {
if(hourlo <= 8 || hourlo == 12) {
hourlo++;
} else {
hourlo = !(hourlo & 1);
hourhi++;
}
} else {
if(hourlo != 3 && !(hourlo & 4)) {
if(hourlo <= 8 || hourlo >= 12) {
hourlo++;
} else {
hourlo = !(hourlo & 1);
hourhi++;
}
} else {
hourlo = !(hourlo & 1);
hourhi = 0;
tick_day();
}
}
} else {
if(hourhi == 0) {
if(hourlo <= 8 || hourlo == 12) {
hourlo++;
} else {
hourlo = !(hourlo & 1);
hourhi ^= 1;
}
} else {
if(hourlo & 1) meridian ^= 1;
if(hourlo < 2 || hourlo == 4 || hourlo == 5 || hourlo == 8 || hourlo == 12) {
hourlo++;
} else {
hourlo = !(hourlo & 1);
hourhi ^= 1;
}
if(meridian == 0 && !(hourlo & 1)) tick_day();
}
}
}
auto EpsonRTC::tick_day() -> void {
if(calendar == 0) return;
weekday = (weekday + 1) + (weekday == 6);
//January - December = 0x01 - 0x09; 0x10 - 0x12
static const uint daysinmonth[32] = {
30, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30, 31, 30,
31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30,
};
uint days = daysinmonth[monthhi << 4 | monthlo];
if(days == 28) {
//add one day for leap years
if((yearhi & 1) == 0 && ((yearlo - 0) & 3) == 0) days++;
if((yearhi & 1) == 1 && ((yearlo - 2) & 3) == 0) days++;
}
if(days == 28 && (dayhi == 3 || (dayhi == 2 && daylo >= 8))) {
daylo = 1;
dayhi = 0;
return tick_month();
}
if(days == 29 && (dayhi == 3 || (dayhi == 2 && (daylo > 8 && daylo != 12)))) {
daylo = 1;
dayhi = 0;
return tick_month();
}
if(days == 30 && (dayhi == 3 || (dayhi == 2 && (daylo == 10 || daylo == 14)))) {
daylo = 1;
dayhi = 0;
return tick_month();
}
if(days == 31 && (dayhi == 3 && (daylo & 3))) {
daylo = 1;
dayhi = 0;
return tick_month();
}
if(daylo <= 8 || daylo == 12) {
daylo++;
} else {
daylo = !(daylo & 1);
dayhi++;
}
}
auto EpsonRTC::tick_month() -> void {
if(monthhi == 0 || !(monthlo & 2)) {
if(monthlo <= 8 || monthlo == 12) {
monthlo++;
} else {
monthlo = !(monthlo & 1);
monthhi ^= 1;
}
} else {
monthlo = !(monthlo & 1);
monthhi = 0;
tick_year();
}
}
auto EpsonRTC::tick_year() -> void {
if(yearlo <= 8 || yearlo == 12) {
yearlo++;
} else {
yearlo = !(yearlo & 1);
if(yearhi <= 8 || yearhi == 12) {
yearhi++;
} else {
yearhi = !(yearhi & 1);
}
}
}