Update to v089r07 release.

byuu says:

Changelog:
- EpsonRTC emulation improved further (stop/pause blocks IRQs, verified
  secondhi >= 3 triggers 30-second adjust (even on invalid BCD),
  second-changed flag is mirrored to minute+hour+day+month+weekday,
  improved busy timing, etc.)
- SharpRTC rewritten, works like EpsonRTC now in that it has its own
  timing thread and ticks with the emulation
- won't attempt to read from an unopen file stream now (I think this is
  what was crashing Sufami Turbo without SRAM?)
- added Tools -> Synchronize Time option below load/save state options.
  Only appears when you play a game with an emulated RTC chip

Just realized that I used 125ms for the 30-second adjust instead of
125us, so I'll fix that in the next WIP.
Aside from that, this is as good as the emulation is going to get.
There's still a couple of absolutely psychopathic edge cases that are
just too damn difficult to simulate.
So that leaves us with data port control + decompression status
registers to investigate before SPC7110 will be finished.
This commit is contained in:
Tim Allen 2012-05-23 21:27:45 +10:00
parent d6001a2df4
commit 5dbd5f4d0f
19 changed files with 441 additions and 200 deletions

View File

@ -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";
}

View File

@ -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;

View File

@ -163,14 +163,6 @@ SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size)
" <map address='00-3f:4800-483f'/>\n"
" <map address='80-bf:4800-483f'/>\n"
" </mmio>\n"
);
if(has_spc7110rtc) markup.append(
" <rtc>\n"
" <map address='00-3f:4840-4842'/>\n"
" <map address='80-bf:4840-4842'/>\n"
" </rtc>\n"
);
markup.append(
" <dcu>\n"
" <map address='50:0000-ffff'/>\n"
" </dcu>\n"
@ -189,6 +181,12 @@ SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size)
" </mcu>\n"
" </spc7110>\n"
);
if(has_spc7110rtc) markup.append(
" <epsonrtc>\n"
" <map address='00-3f:4840-4842'/>\n"
" <map address='80-bf:4840-4842'/>\n"
" </epsonrtc>\n"
);
}
else if(mapper == LoROM) {
@ -394,10 +392,10 @@ SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size)
);
if(has_srtc) markup.append(
" <srtc>\n"
" <sharprtc>\n"
" <map address='00-3f:2800-2801'/>\n"
" <map address='80-bf:2800-2801'/>\n"
" </srtc>\n"
" </sharprtc>\n"
);
if(has_sdd1) markup.append(

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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++) {

View File

@ -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);

View File

@ -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++;

View File

@ -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;
}
}

View File

@ -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

View File

@ -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<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 = 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;

View File

@ -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;

View File

@ -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
}

View File

@ -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();

View File

@ -48,6 +48,9 @@ struct Interface : Emulator::Interface {
void reset();
void run();
bool rtc();
void rtcsync();
serializer serialize();
bool unserialize(serializer&);

View File

@ -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);

View File

@ -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(); };

View File

@ -43,6 +43,7 @@ struct Presentation : Window {
Item saveStateItem[5];
Menu loadStateMenu;
Item loadStateItem[5];
Item synchronizeTime;
Separator stateMenuSeparator;
Item resizeWindow;
Item cheatEditor;

View File

@ -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);
}