diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index 1af327b74..1a50ae442 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.04"; + static const char Version[] = "089.05"; static const char Author[] = "byuu"; static const char License[] = "GPLv3"; } diff --git a/bsnes/sfc/Makefile b/bsnes/sfc/Makefile index b48f3466d..e02748077 100755 --- a/bsnes/sfc/Makefile +++ b/bsnes/sfc/Makefile @@ -3,7 +3,7 @@ sfc_objects += sfc-cartridge sfc-cheat sfc_objects += sfc-memory sfc-cpu sfc-smp sfc-dsp sfc-ppu sfc_objects += sfc-icd2 sfc-nss sfc-superfx sfc-sa1 sfc_objects += sfc-necdsp sfc-hitachidsp sfc-armdsp -sfc_objects += sfc-bsx sfc-srtc sfc-sdd1 sfc-spc7110 +sfc_objects += sfc-bsx sfc-srtc sfc-sdd1 sfc-spc7110 sfc-rtc4513 sfc_objects += sfc-obc1 sfc-sufamiturbo sfc_objects += sfc-msu1 sfc-link objects += $(sfc_objects) @@ -50,6 +50,7 @@ obj/sfc-bsx.o : $(sfc)/chip/bsx/bsx.cpp $(call rwildcard,$(sfc)/chip/bsx/ obj/sfc-srtc.o : $(sfc)/chip/srtc/srtc.cpp $(sfc)/chip/srtc/* obj/sfc-sdd1.o : $(sfc)/chip/sdd1/sdd1.cpp $(sfc)/chip/sdd1/* obj/sfc-spc7110.o : $(sfc)/chip/spc7110/spc7110.cpp $(sfc)/chip/spc7110/* +obj/sfc-rtc4513.o : $(sfc)/chip/rtc4513/rtc4513.cpp $(sfc)/chip/rtc4513/* obj/sfc-obc1.o : $(sfc)/chip/obc1/obc1.cpp $(sfc)/chip/obc1/* obj/sfc-sufamiturbo.o: $(sfc)/chip/sufamiturbo/sufamiturbo.cpp $(sfc)/chip/sufamiturbo/* obj/sfc-msu1.o : $(sfc)/chip/msu1/msu1.cpp $(sfc)/chip/msu1/* diff --git a/bsnes/sfc/cartridge/cartridge.cpp b/bsnes/sfc/cartridge/cartridge.cpp index 750956563..d9ecb2e25 100755 --- a/bsnes/sfc/cartridge/cartridge.cpp +++ b/bsnes/sfc/cartridge/cartridge.cpp @@ -27,7 +27,7 @@ void Cartridge::load(const string &markup, const stream &stream) { has_srtc = false; has_sdd1 = false; has_spc7110 = false; - has_spc7110rtc = false; + has_rtc4513 = false; has_obc1 = false; has_msu1 = false; has_link = false; diff --git a/bsnes/sfc/cartridge/cartridge.hpp b/bsnes/sfc/cartridge/cartridge.hpp index d81f0ab8b..3633b5d6e 100755 --- a/bsnes/sfc/cartridge/cartridge.hpp +++ b/bsnes/sfc/cartridge/cartridge.hpp @@ -35,7 +35,7 @@ struct Cartridge : property { readonly has_srtc; readonly has_sdd1; readonly has_spc7110; - readonly has_spc7110rtc; + readonly has_rtc4513; readonly has_obc1; readonly has_msu1; readonly has_link; @@ -82,6 +82,7 @@ private: void parse_markup_srtc(XML::Node&); void parse_markup_sdd1(XML::Node&); void parse_markup_spc7110(XML::Node&); + void parse_markup_rtc4513(XML::Node&); void parse_markup_obc1(XML::Node&); void parse_markup_msu1(XML::Node&); void parse_markup_link(XML::Node&); diff --git a/bsnes/sfc/cartridge/markup.cpp b/bsnes/sfc/cartridge/markup.cpp index cec6d075f..3cd5e3d6c 100755 --- a/bsnes/sfc/cartridge/markup.cpp +++ b/bsnes/sfc/cartridge/markup.cpp @@ -21,6 +21,7 @@ void Cartridge::parse_markup(const char *markup) { parse_markup_srtc(cartridge["srtc"]); parse_markup_sdd1(cartridge["sdd1"]); parse_markup_spc7110(cartridge["spc7110"]); + parse_markup_rtc4513(cartridge["rtc4513"]); parse_markup_obc1(cartridge["obc1"]); parse_markup_msu1(cartridge["msu1"]); parse_markup_link(cartridge["link"]); @@ -398,10 +399,8 @@ void Cartridge::parse_markup_sdd1(XML::Node &root) { void Cartridge::parse_markup_spc7110(XML::Node &root) { if(root.exists() == false) return; has_spc7110 = true; - has_spc7110rtc = root["rtc"].exists(); auto &mmio = root["mmio"]; - auto &rtc = root["rtc"]; auto &dcu = root["dcu"]; auto &mcurom = root["mcu"]["rom"]; auto &mcuram = root["mcu"]["ram"]; @@ -421,13 +420,6 @@ void Cartridge::parse_markup_spc7110(XML::Node &root) { mapping.append(m); } - for(auto &node : rtc) { - if(node.name != "map") continue; - Mapping m({&SPC7110::mmio_read, &spc7110}, {&SPC7110::mmio_write, &spc7110}); - parse_markup_map(m, node); - mapping.append(m); - } - for(auto &node : dcu) { if(node.name != "map") continue; Mapping m({&SPC7110::dcu_read, &spc7110}, {&SPC7110::dcu_write, &spc7110}); @@ -450,6 +442,18 @@ void Cartridge::parse_markup_spc7110(XML::Node &root) { } } +void Cartridge::parse_markup_rtc4513(XML::Node &root) { + if(root.exists() == false) return; + has_rtc4513 = true; + + for(auto &node : root) { + if(node.name != "map") continue; + Mapping m({&RTC4513::read, &rtc4513}, {&RTC4513::write, &rtc4513}); + parse_markup_map(m, node); + mapping.append(m); + } +} + void Cartridge::parse_markup_obc1(XML::Node &root) { if(root.exists() == false) return; has_obc1 = true; diff --git a/bsnes/sfc/chip/chip.hpp b/bsnes/sfc/chip/chip.hpp index 3081b91dd..bded8ed6d 100755 --- a/bsnes/sfc/chip/chip.hpp +++ b/bsnes/sfc/chip/chip.hpp @@ -14,6 +14,7 @@ struct Coprocessor : Thread { #include #include #include +#include #include #include #include diff --git a/bsnes/sfc/chip/rtc4513/memory.cpp b/bsnes/sfc/chip/rtc4513/memory.cpp new file mode 100755 index 000000000..db18b62ad --- /dev/null +++ b/bsnes/sfc/chip/rtc4513/memory.cpp @@ -0,0 +1,162 @@ +#ifdef RTC4513_CPP + +unsigned RTC4513::second() { return secondlo + secondhi * 10; } +unsigned RTC4513::minute() { return minutelo + minutehi * 10; } +unsigned RTC4513::hour () { return hourlo + hourhi * 10; } +unsigned RTC4513::day () { return daylo + dayhi * 10; } +unsigned RTC4513::month () { return monthlo + monthhi * 10; } +unsigned RTC4513::year () { return yearlo + yearhi * 10; } + +void RTC4513::second(unsigned data) { secondlo = data % 10; secondhi = (data / 10) % 10; } +void RTC4513::minute(unsigned data) { minutelo = data % 10; minutehi = (data / 10) % 10; } +void RTC4513::hour (unsigned data) { hourlo = data % 10; hourhi = (data / 10) % 10; } +void RTC4513::day (unsigned data) { daylo = data % 10; dayhi = (data / 10) % 10; } +void RTC4513::month (unsigned data) { monthlo = data % 10; monthhi = (data / 10) % 10; } +void RTC4513::year (unsigned data) { yearlo = data % 10; yearhi = (data / 10) % 10; } + +void RTC4513::rtc_reset() { + state = State::Mode; + offset = 0; + + pause = 0; + test = 0; + minutecarry = 0; +} + +uint4 RTC4513::rtc_read(uint4 addr) { + switch(addr) { + case 0: return secondlo; + case 1: return secondhi | batteryfailure << 3; + case 2: return minutelo; + case 3: return minutehi | minutecarry << 3; + case 4: return hourlo; + case 5: return hourhi | meridian << 2 | hourcarry << 3; + case 6: return daylo; + case 7: return dayhi | dayram << 2 | daycarry << 3; + case 8: return monthlo; + case 9: return monthhi | monthram << 1 | monthcarry << 3; + case 10: return yearlo; + case 11: return yearhi; + case 12: return weekday | weekdaycarry << 3; + case 13: { + uint1 readflag = irqflag & irqmask; + irqflag = 0; + return hold | calendar << 1 | readflag << 2 | roundseconds << 3; + } + case 14: return irqmask | irqduty << 1 | irqperiod << 2; + case 15: return pause | stop << 1 | atime << 2 | test << 3; + } +} + +void RTC4513::rtc_write(uint4 addr, uint4 data) { + switch(addr) { + case 0: + secondlo = data; + break; + case 1: + secondhi = data; + batteryfailure = data >> 3; + break; + case 2: + minutelo = data; + break; + case 3: + minutehi = data; + minutecarry = data >> 3; + break; + case 4: + hourlo = data; + break; + case 5: + hourhi = data; + meridian = data >> 2; + hourcarry = data >> 3; + if(atime == 1) meridian = 0; + if(atime == 0) hourhi &= 1; + break; + case 6: + daylo = data; + break; + case 7: + dayhi = data; + dayram = data >> 2; + daycarry = data >> 3; + break; + case 8: + monthlo = data; + break; + case 9: + monthhi = data; + monthram = data >> 1; + monthcarry = data >> 3; + break; + case 10: + yearlo = data; + break; + case 11: + yearhi = data; + break; + case 12: + weekday = data; + weekdaycarry = data >> 3; + break; + case 13: + hold = data; + calendar = data >> 1; + //irqflag cannot be set manually + roundseconds = data >> 3; + if(roundseconds) { + roundseconds = 0; + if(second() >= 30) tick_minute(); + second(0); + } + break; + case 14: + irqmask = data; + irqduty = data >> 1; + irqperiod = data >> 2; + break; + case 15: + pause = data; + stop = data >> 1; + atime = data >> 2; + test = data >> 3; + if(atime == 1) meridian = 0; + if(atime == 0) hourhi &= 1; + if(pause) second(0); + break; + } +} + +void RTC4513::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 RTC4513::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; + } +} + +#endif diff --git a/bsnes/sfc/chip/rtc4513/rtc4513.cpp b/bsnes/sfc/chip/rtc4513/rtc4513.cpp new file mode 100755 index 000000000..b5b130a1a --- /dev/null +++ b/bsnes/sfc/chip/rtc4513/rtc4513.cpp @@ -0,0 +1,131 @@ +#include + +#define RTC4513_CPP +namespace SuperFamicom { + +#include "memory.cpp" +#include "time.cpp" +#include "serialization.cpp" +RTC4513 rtc4513; + +void RTC4513::Enter() { + rtc4513.enter(); +} + +void RTC4513::enter() { + while(true) { + if(scheduler.sync == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + 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 == 0) { //1 second + seconds++; + irq(1); + if(seconds % 60 == 0) irq(2); //1 minute + if(seconds % 1440 == 0) irq(3); //1 hour + if(seconds == 1440) seconds = 0; + tick(); + } + + step(1); + synchronize_cpu(); + } +} + +void RTC4513::init() { +} + +void RTC4513::load() { + if(cartridge.has_rtc4513()) interface->memory.append({ID::RTC4513, "rtc.ram"}); + batteryfailure = 1; +} + +void RTC4513::unload() { +} + +void RTC4513::power() { +} + +void RTC4513::reset() { + create(RTC4513::Enter, 32768); + + clocks = 0; + seconds = 0; + + chipselect = 0; + state = State::Mode; + offset = 0; + wait = 0; + ready = false; +} + +uint8 RTC4513::read(unsigned addr) { + cpu.synchronize_coprocessors(); + addr &= 3; + + if(addr == 0) { + return chipselect; + } + + if(addr == 1) { + if(chipselect != 1) return 0; + if(ready == false) return 0; + if(state == State::Write) return mdr; + if(state != State::Read) return 0; + ready = false; + wait = 1; + return rtc_read(offset++); + } + + if(addr == 2) { + return ready << 7; + } +} + +void RTC4513::write(unsigned addr, uint8 data) { + cpu.synchronize_coprocessors(); + addr &= 3, data &= 15; + + if(addr == 0) { + chipselect = data; + if(chipselect != 1) rtc_reset(); + ready = true; + } + + if(addr == 1) { + if(chipselect != 1) return; + if(ready == false) return; + + if(state == State::Mode) { + if(data != 0x03 && data != 0x0c) return; + state = State::Seek; + ready = false; + wait = 1; + mdr = data; + } + + else if(state == State::Seek) { + if(mdr == 0x03) state = State::Write; + if(mdr == 0x0c) state = State::Read; + + offset = data; + ready = false; + wait = 1; + mdr = data; + } + + else if(state == State::Write) { + rtc_write(offset++, data); + ready = false; + wait = 1; + mdr = data; + } + } +} + +} diff --git a/bsnes/sfc/chip/rtc4513/rtc4513.hpp b/bsnes/sfc/chip/rtc4513/rtc4513.hpp new file mode 100755 index 000000000..07dc69dad --- /dev/null +++ b/bsnes/sfc/chip/rtc4513/rtc4513.hpp @@ -0,0 +1,105 @@ +//Epson RTC-4513 Real-Time Clock + +struct RTC4513 : Coprocessor { + static void Enter(); + void enter(); + + void init(); + void load(); + void unload(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + + uint15 clocks; + unsigned seconds; + + uint2 chipselect; + enum class State : unsigned { Mode, Seek, Read, Write } state; + uint4 mdr; + uint4 offset; + unsigned wait; + bool ready; + + uint4 secondlo; + uint3 secondhi; + uint1 batteryfailure; + + uint4 minutelo; + uint3 minutehi; + uint1 minutecarry; + + 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; + uint1 irqflag; + uint1 roundseconds; + + uint1 irqmask; + uint1 irqduty; + uint2 irqperiod; + + uint1 pause; + uint1 stop; + uint1 atime; //astronomical time (24-hour mode) + uint1 test; + + //memory.cpp + unsigned second(); + unsigned minute(); + unsigned hour(); + unsigned day(); + unsigned month(); + unsigned year(); + + void second(unsigned); + void minute(unsigned); + void hour(unsigned); + void day(unsigned); + void month(unsigned); + void year(unsigned); + + void rtc_reset(); + uint4 rtc_read(uint4 addr); + void rtc_write(uint4 addr, uint4 data); + + void load(const uint8 *data); + void save(uint8 *data); + + //time.cpp + void irq(uint2 period); + void duty(); + void tick(); + void tick_second(); + void tick_minute(); + void tick_hour(); + void tick_day(); + void tick_month(); + void tick_year(); +}; + +extern RTC4513 rtc4513; diff --git a/bsnes/sfc/chip/rtc4513/serialization.cpp b/bsnes/sfc/chip/rtc4513/serialization.cpp new file mode 100755 index 000000000..1b6cd4e9f --- /dev/null +++ b/bsnes/sfc/chip/rtc4513/serialization.cpp @@ -0,0 +1,60 @@ +#ifdef RTC4513_CPP + +void RTC4513::serialize(serializer &s) { + Thread::serialize(s); + + s.integer(clocks); + s.integer(seconds); + + s.integer(chipselect); + s.integer((unsigned&)state); + s.integer(mdr); + s.integer(offset); + s.integer(wait); + s.integer(ready); + + s.integer(secondlo); + s.integer(secondhi); + s.integer(batteryfailure); + + s.integer(minutelo); + s.integer(minutehi); + s.integer(minutecarry); + + 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); + s.integer(irqflag); + s.integer(roundseconds); + + s.integer(irqmask); + s.integer(irqduty); + s.integer(irqperiod); + + s.integer(pause); + s.integer(stop); + s.integer(atime); + s.integer(test); +} + +#endif diff --git a/bsnes/sfc/chip/rtc4513/time.cpp b/bsnes/sfc/chip/rtc4513/time.cpp new file mode 100755 index 000000000..6ba43985a --- /dev/null +++ b/bsnes/sfc/chip/rtc4513/time.cpp @@ -0,0 +1,73 @@ +#ifdef RTC4513_CPP + +void RTC4513::irq(uint2 period) { + if(period == irqperiod) irqflag = 1; +} + +void RTC4513::duty() { + if(irqduty) irqflag = 0; +} + +void RTC4513::tick() { + if(hold) return; + if(pause) return; + if(stop) return; + + minutecarry = true; + tick_second(); +} + +void RTC4513::tick_second() { + if(second() < 59) return second(second() + 1); + second(0); + tick_minute(); +} + +void RTC4513::tick_minute() { + if(minute() < 59) return minute(minute() + 1); + minute(0); + tick_hour(); +} + +void RTC4513::tick_hour() { + if(atime) { + if(hour() < 23) return hour(hour() + 1); + hour(0); + tick_day(); + } + + else { + if(hour() < 12) return hour(hour() + 1); + hour(1); + meridian ^= 1; + if(meridian == 0) tick_day(); + } +} + +void RTC4513::tick_day() { + if(calendar == false) return; + + static const unsigned daysinmonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + unsigned days = daysinmonth[month() - 1]; + if(year() % 4 == 0 && month() == 2) days++; + + if(day() < days) return day(day() + 1); + day(1); + + if(weekday < 6) weekday++; + else weekday = 0; + + tick_month(); +} + +void RTC4513::tick_month() { + if(month() < 12) return month(month() + 1); + month(1); + tick_year(); +} + +void RTC4513::tick_year() { + year(year() + 1); +} + +#endif diff --git a/bsnes/sfc/chip/spc7110/rtc.cpp b/bsnes/sfc/chip/spc7110/rtc.cpp deleted file mode 100755 index 5386137b9..000000000 --- a/bsnes/sfc/chip/spc7110/rtc.cpp +++ /dev/null @@ -1,252 +0,0 @@ -#ifdef SPC7110_CPP - -//OFS NAME BIT:3 BIT:2 BIT:1 BIT:0 -//--- ---- ----- ----- ----- ----- -//0x0 S01 SEC3 SEC2 SEC1 SEC0 -//0x1 S10 LOST SEC6 SEC5 SEC4 -//0x2 M01 MIN3 MIN2 MIN1 MIN0 -//0x3 M10 WRAP MIN6 MIN5 MIN4 -//0x4 H01 HOUR3 HOUR2 HOUR1 HOUR0 -//0x5 H10 WRAP AM/PM HOUR5 HOUR4 -//0x6 D01 DAY3 DAY2 DAY1 DAY0 -//0x7 D10 WRAP RAM0 DAY5 DAY4 -//0x8 MO01 MON3 MON2 MON1 MON0 -//0x9 MO10 WRAP RAM2 RAM1 MON4 -//0xa Y01 YEAR3 YEAR2 YEAR1 YEAR0 -//0xb Y10 YEAR7 YEAR6 YEAR5 YEAR4 -//0xc WDAY WRAP WEEK2 WEEK1 WEEK0 -//0xd CD 30ADJ IRQF CAL HOLD -//0xe CE RATE1 RATE0 DUTY MASK -//0xf CF TEST 24/12 STOP RESET - -void SPC7110::rtc_reset() { - rtc_mode = 0; - rtc_addr = 0; - - rtcram[0xf] &= ~1; //clear reset - rtcram[0xf] &= ~8; //clear test - - rtcram[0x3] &= ~8; //clear wrap -//rtcram[0x5] &= ~8; //clear wrap -//if((rtcram[0xd] & 2) == 0) return; //calendar mode disabled (bits are RAM) - -//rtcram[0x7] &= ~8; //clear wrap -//rtcram[0x9] &= ~8; //clear wrap -//rtcram[0xc] &= ~8; //clear wrap -} - -void SPC7110::rtc_duty() { - if(rtcram[0xe] & 2) rtcram[0xd] &= ~4; -} - -void SPC7110::rtc_irq(uint2 frequency) { - uint2 rate = rtcram[0xe] >> 2; - if(frequency != rate) return; - rtcram[0xd] |= 4; -} - -uint4 SPC7110::rtc_read(uint4 addr) { - switch(addr) { default: - case 0x0: return rtcram[0x0]; - case 0x1: return rtcram[0x1]; - case 0x2: return rtcram[0x2]; - case 0x3: return rtcram[0x3]; - case 0x4: return rtcram[0x4]; - case 0x5: return rtcram[0x5]; - case 0x6: return rtcram[0x6]; - case 0x7: return rtcram[0x7]; - case 0x8: return rtcram[0x8]; - case 0x9: return rtcram[0x9]; - case 0xa: return rtcram[0xa]; - case 0xb: return rtcram[0xb]; - case 0xc: return rtcram[0xc]; - case 0xd: { - uint4 data = rtcram[0xd]; - if(rtcram[0xe] & 1) data &= ~4; //force irq flag clear if mask is set - rtcram[0xd] &= ~4; //always clear irq flag on read (acknowledge pending IRQ) - return data; - } - case 0xe: return rtcram[0xe]; - case 0xf: return rtcram[0xf]; - } -} - -void SPC7110::rtc_write(uint4 addr, uint4 data) { - switch(addr) { - case 0x0: rtcram[0x0] = data; break; - case 0x1: rtcram[0x1] = data; break; - case 0x2: rtcram[0x2] = data; break; - case 0x3: rtcram[0x3] = data; break; - case 0x4: rtcram[0x4] = data; break; - case 0x5: - if((rtcram[0xf] & 4) == 0) rtcram[0x5] = data & ~2; //12-hour mode cannot set D5 - if((rtcram[0xf] & 4) != 0) rtcram[0x5] = data & ~4; //24-hour mode cannot set AM/PM - break; - case 0x6: rtcram[0x6] = data; break; - case 0x7: rtcram[0x7] = data; break; - case 0x8: rtcram[0x8] = data; break; - case 0x9: rtcram[0x9] = data; break; - case 0xa: rtcram[0xa] = data; break; - case 0xb: rtcram[0xb] = data; break; - case 0xc: rtcram[0xc] = data; break; - case 0xd: rtcram[0xd] = (rtcram[0xd] & 4) | (data & ~4); //irq flag is read-only - if(data & 8) { //round to nearest minute - unsigned second = (rtcram[0x1] & 7) * 10 + rtcram[0x0]; - rtcram[0x0] &= 0; - rtcram[0x1] &= 8; - if(second >= 30) rtc_minute(); - } - break; - case 0xe: rtcram[0xe] = data; break; - case 0xf: rtcram[0xf] = data; - if(data & 1) { - //clear seconds - rtcram[0x0] &= 0; - rtcram[0x1] &= 8; - } - break; - } -} - -void SPC7110::rtc_pulse() { - if(rtcram[0xd] & 1) return; //clock hold - if(rtcram[0xf] & 1) return; //clock reset - if(rtcram[0xf] & 2) return; //clock stop - - //set wrap flags (time changed since last select) - rtcram[0x3] |= 8; -//rtcram[0x5] |= 8; -//if(rtcram[0xd] & 2) { -// rtcram[0x7] |= 8; -// rtcram[0x9] |= 8; -// rtcram[0xc] |= 8; -//} - - rtc_second(); -} - -void SPC7110::rtc_second() { - unsigned second = (rtcram[0x1] & 0x7) * 10 + rtcram[0x0]; - - if(++second > 59) second = 0; - rtcram[0x0] = second % 10; - rtcram[0x1] = (rtcram[0x1] & 8) | ((second / 10) & 7); - - if(second == 0) rtc_minute(); -} - -void SPC7110::rtc_minute() { - unsigned minute = (rtcram[0x3] & 0x7) * 10 + rtcram[0x2]; - - if(++minute > 59) minute = 0; - rtcram[0x2] = minute % 10; - rtcram[0x3] = (rtcram[0x3] & 8) | ((minute / 10) & 7); - - if(minute == 0) rtc_hour(); -} - -void SPC7110::rtc_hour() { - unsigned hour = (rtcram[0x5] & 3) * 10 + rtcram[0x4]; - - if(rtcram[0xf] & 4) { - //24-hour mode (00-23) - if(++hour > 23) hour = 0; - rtcram[0x4] = hour % 10; - rtcram[0x5] = (rtcram[0x5] & 12) | ((hour / 10) & 3); - if(hour == 0) rtc_day(); - } else { - //12-hour mode (01-12) - if(++hour > 12) hour = 1; - rtcram[0x4] = hour % 10; - rtcram[0x5] = (rtcram[0x5] & 12) | ((hour / 10) & 3); - if(hour == 12) { - rtcram[0x5] ^= 4; //toggle meridian - if((rtcram[0x5] & 4) == 0) rtc_day(); - } - } -} - -void SPC7110::rtc_day() { - //calendar disable - if((rtcram[0xd] & 2) == 0) return; - - //calendar - static const unsigned daysinmonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - - unsigned year = rtcram[0xb] * 10 + rtcram[0xa]; - unsigned month = (rtcram[0x9] & 1) * 10 + rtcram[0x8]; - unsigned day = (rtcram[0x7] & 3) * 10 + rtcram[0x6]; - unsigned weekday = rtcram[0xc] & 7; - unsigned days = daysinmonth[month]; - - //add leap year day if necessary - //range is ~199x-209x; so year %400 -> year % 100 rules are unnecessary - if(year % 4 == 0 && month == 2) days++; - - //day (01-31) - if(++day > days) day = 1; - rtcram[0x6] = day % 10; - rtcram[0x7] = (rtcram[0x7] & 12) | ((day / 10) & 3); - - if(++weekday > 6) weekday = 0; - rtcram[0xc] = (rtcram[0xc] & 8) | (weekday & 7); - - if(day == 1) rtc_month(); -} - -void SPC7110::rtc_month() { - //month (01-12) - unsigned month = (rtcram[0x9] & 1) * 10 + rtcram[0x8]; - - if(++month > 12) month = 1; - rtcram[0x8] = month % 10; - rtcram[0x9] = (rtcram[0x9] & 14) | ((month / 12) & 1); - - if(month == 1) rtc_year(); -} - -void SPC7110::rtc_year() { - //year (00-99) - unsigned year = rtcram[0xb] * 10 + rtcram[0xa]; - - if(++year > 99) year = 0; - rtcram[0xa] = year % 10; - rtcram[0xb] = year / 10; -} - -void SPC7110::rtcram_load(const uint8 *data) { - uint64 timestamp = 0; - - for(unsigned n = 0; n < 8; n++) { - rtcram[n * 2 + 0] = data[n] >> 0; - rtcram[n * 2 + 1] = data[n] >> 4; - } - - for(unsigned n = 0; n < 8; n++) { - timestamp |= data[8 + n] << (n * 8); - } - - //determine the number of seconds that have passed since the last time the - //RTC state was saved ... and add that many seconds to the saved RTC time. - uint64 diff = (uint64)time(0) - timestamp; - while(diff >= 60 * 60 * 24) { rtc_day(); diff -= 60 * 60 * 24; } - while(diff >= 60 * 60) { rtc_hour(); diff -= 60 * 60; } - while(diff >= 60) { rtc_minute(); diff -= 60; } - while(diff--) rtc_second(); -} - -void SPC7110::rtcram_save(uint8 *data) { - uint64 timestamp = (uint64)time(0); - - for(unsigned n = 0; n < 8; n++) { - data[n] = rtcram[n * 2 + 0] << 0; - data[n] |= rtcram[n * 2 + 1] << 4; - } - - for(unsigned n = 0; n < 8; n++) { - data[8 + n] = timestamp; - timestamp >>= 8; - } -} - -#endif diff --git a/bsnes/sfc/chip/spc7110/serialization.cpp b/bsnes/sfc/chip/spc7110/serialization.cpp index 3aee8f6e1..180959a67 100755 --- a/bsnes/sfc/chip/spc7110/serialization.cpp +++ b/bsnes/sfc/chip/spc7110/serialization.cpp @@ -1,8 +1,6 @@ #ifdef SPC7110_CPP void SPC7110::serialize(serializer &s) { - for(auto &byte : rtcram) s.integer(byte); - s.integer(r4801); s.integer(r4802); s.integer(r4803); @@ -66,17 +64,6 @@ void SPC7110::serialize(serializer &s) { s.integer(r4832); s.integer(r4833); s.integer(r4834); - - s.integer(r4840); - s.integer(r4841); - s.integer(r4842); - - s.integer(rtc_clocks); - s.integer(rtc_seconds); - s.integer(rtc_mode); - s.integer(rtc_addr); - s.integer(rtc_wait); - s.integer(rtc_mdr); } #endif diff --git a/bsnes/sfc/chip/spc7110/spc7110.cpp b/bsnes/sfc/chip/spc7110/spc7110.cpp index 8872ec89a..50fecbe67 100755 --- a/bsnes/sfc/chip/spc7110/spc7110.cpp +++ b/bsnes/sfc/chip/spc7110/spc7110.cpp @@ -6,7 +6,6 @@ namespace SuperFamicom { #include "dcu.cpp" #include "data.cpp" #include "alu.cpp" -#include "rtc.cpp" #include "serialization.cpp" SPC7110 spc7110; @@ -20,19 +19,6 @@ void SPC7110::enter() { if(mul_wait) { if(--mul_wait == 0) alu_multiply(); } if(div_wait) { if(--div_wait == 0) alu_divide(); } - if(rtc_wait) { if(--rtc_wait == 0) r4842 |= 0x80; } - - rtc_clocks++; - if((rtc_clocks & 0x7fff) == 0) rtc_duty(); //1/128th second - if((rtc_clocks & 0xffff) == 0) rtc_irq(0); //1/ 64th second - if(rtc_clocks == 0) { //1 second - rtc_irq(1); - rtc_seconds++; - if(rtc_seconds % 60 == 0) rtc_irq(2); //1 minute - if(rtc_seconds % 1440 == 0) rtc_irq(3); //1 hour - if(rtc_seconds == 1440) rtc_seconds = 0; - rtc_pulse(); - } step(1); synchronize_cpu(); @@ -43,10 +29,6 @@ void SPC7110::init() { } void SPC7110::load() { - if(cartridge.has_spc7110rtc()) interface->memory.append({ID::SPC7110RTC, "rtc.ram"}); - - for(auto &byte : rtcram) byte = 0x00; - rtcram[0x1] |= 8; //set lost flag (battery back-up failure) } void SPC7110::unload() { @@ -114,17 +96,6 @@ void SPC7110::reset() { r4832 = 0x01; r4833 = 0x02; r4834 = 0x00; - - r4840 = 0x00; - r4841 = 0x00; - r4842 = 0x00; - - rtc_clocks = 0; - rtc_seconds = 0; - rtc_mode = 0; - rtc_addr = 0; - rtc_wait = 0; - rtc_mdr = 0; } uint8 SPC7110::mmio_read(unsigned addr) { @@ -215,23 +186,6 @@ uint8 SPC7110::mmio_read(unsigned addr) { case 0x4833: return r4833; case 0x4834: return r4834; - //==================== - //real-time clock unit - //==================== - - case 0x4840: return r4840; - case 0x4841: { - if(r4840 != 1) return 0x00; //RTC disabled? - if(r4842 == 0) return 0x00; //RTC busy? - if(rtc_mode != 2) return 0x00; //waiting for command or address? - if(r4841 == 0x03) return rtc_mdr; //in write mode? - if(r4841 != 0x0c) return 0x00; //not in read mode? - r4842 |= 0x80; - rtc_wait = rtc_delay; - return rtc_read(rtc_addr++); - } - case 0x4842: return r4842; - } return cpu.regs.mdr; @@ -303,42 +257,6 @@ void SPC7110::mmio_write(unsigned addr, uint8 data) { case 0x4833: r4833 = data & 0x07; break; case 0x4834: r4834 = data & 0x07; break; - //==================== - //real-time clock unit - //==================== - - case 0x4840: r4840 = data & 0x03; { - if(r4840 != 1) rtc_reset(); - r4842 |= 0x80; - } break; - - case 0x4841: { - if(r4840 != 1) break; //RTC disabled? - if(r4842 == 0) break; //RTC busy? - - r4842 |= 0x80; - rtc_wait = rtc_delay; - rtc_mdr = data; - - if(rtc_mode == 0) { - rtc_mode = 1; - rtc_addr = 0; - r4841 = rtc_mdr; - break; - } - - if(rtc_mode == 1) { - rtc_mode = 2; - rtc_addr = rtc_mdr; - break; - } - - if(r4841 == 0x03) { - rtc_write(rtc_addr++, rtc_mdr); - break; - } - } - } } diff --git a/bsnes/sfc/chip/spc7110/spc7110.hpp b/bsnes/sfc/chip/spc7110/spc7110.hpp index 584ff432e..33ff69859 100755 --- a/bsnes/sfc/chip/spc7110/spc7110.hpp +++ b/bsnes/sfc/chip/spc7110/spc7110.hpp @@ -1,6 +1,7 @@ struct SPC7110 : Coprocessor { unsigned prom_base, prom_size; //program ROM unsigned drom_base, drom_size; //data ROM + uint4 rtcram[16]; enum : unsigned { @@ -73,24 +74,6 @@ struct SPC7110 : Coprocessor { void alu_multiply(); void alu_divide(); - //rtc.cpp - void rtc_reset(); - void rtc_duty(); - void rtc_irq(uint2 frequency); - uint4 rtc_read(uint4 addr); - void rtc_write(uint4 addr, uint4 data); - - void rtc_pulse(); - void rtc_second(); - void rtc_minute(); - void rtc_hour(); - void rtc_day(); - void rtc_month(); - void rtc_year(); - - void rtcram_load(const uint8 *data); - void rtcram_save(uint8 *data); - private: //================== //decompression unit @@ -171,20 +154,6 @@ private: uint8 r4832; //bank 2 mapping uint8 r4833; //bank 3 mapping uint8 r4834; //bank mapping control - - //==================== - //real-time clock unit - //==================== - uint8 r4840; //RTC enable - uint8 r4841; //RTC data port - uint8 r4842; //RTC status (d7 = ready) - - uint22 rtc_clocks; - unsigned rtc_seconds; - unsigned rtc_mode; //0 = command, 1 = index, 2 = read or write - uint4 rtc_addr; - unsigned rtc_wait; - uint4 rtc_mdr; }; extern SPC7110 spc7110; diff --git a/bsnes/sfc/interface/interface.cpp b/bsnes/sfc/interface/interface.cpp index f9944c1ef..efabd6436 100755 --- a/bsnes/sfc/interface/interface.cpp +++ b/bsnes/sfc/interface/interface.cpp @@ -33,7 +33,7 @@ unsigned Interface::group(unsigned id) { case ID::RAM: case ID::NecDSPRAM: case ID::RTC: - case ID::SPC7110RTC: + case ID::RTC4513: case ID::BsxRAM: case ID::BsxPSRAM: return 0; @@ -109,10 +109,10 @@ void Interface::load(unsigned id, const stream &stream, const string &markup) { stream.read(srtc.rtc, min(stream.size(), sizeof srtc.rtc)); } - if(id == ID::SPC7110RTC) { + if(id == ID::RTC4513) { uint8 data[16] = {0}; stream.read(data, min(stream.size(), sizeof data)); - spc7110.rtcram_load(data); + rtc4513.load(data); } if(id == ID::BsxRAM) { @@ -149,9 +149,9 @@ void Interface::save(unsigned id, const stream &stream) { stream.write(srtc.rtc, sizeof srtc.rtc); } - if(id == ID::SPC7110RTC) { + if(id == ID::RTC4513) { uint8 data[16]; - spc7110.rtcram_save(data); + rtc4513.save(data); stream.write(data, sizeof data); } diff --git a/bsnes/sfc/interface/interface.hpp b/bsnes/sfc/interface/interface.hpp index aef7933f2..5d02f97fb 100755 --- a/bsnes/sfc/interface/interface.hpp +++ b/bsnes/sfc/interface/interface.hpp @@ -17,7 +17,7 @@ struct ID { RAM, NecDSPRAM, RTC, - SPC7110RTC, + RTC4513, BsxRAM, BsxPSRAM, SuperGameBoyRAM, diff --git a/bsnes/sfc/system/serialization.cpp b/bsnes/sfc/system/serialization.cpp index ebccf3964..6c4087db9 100755 --- a/bsnes/sfc/system/serialization.cpp +++ b/bsnes/sfc/system/serialization.cpp @@ -68,6 +68,7 @@ void System::serialize_all(serializer &s) { if(cartridge.has_srtc()) srtc.serialize(s); if(cartridge.has_sdd1()) sdd1.serialize(s); if(cartridge.has_spc7110()) spc7110.serialize(s); + if(cartridge.has_rtc4513()) rtc4513.serialize(s); if(cartridge.has_obc1()) obc1.serialize(s); if(cartridge.has_msu1()) msu1.serialize(s); } diff --git a/bsnes/sfc/system/system.cpp b/bsnes/sfc/system/system.cpp index 71cee1e84..898251e87 100755 --- a/bsnes/sfc/system/system.cpp +++ b/bsnes/sfc/system/system.cpp @@ -77,6 +77,7 @@ void System::init() { srtc.init(); sdd1.init(); spc7110.init(); + rtc4513.init(); obc1.init(); msu1.init(); link.init(); @@ -123,6 +124,7 @@ void System::load() { if(cartridge.has_srtc()) srtc.load(); if(cartridge.has_sdd1()) sdd1.load(); if(cartridge.has_spc7110()) spc7110.load(); + if(cartridge.has_rtc4513()) rtc4513.load(); if(cartridge.has_obc1()) obc1.load(); if(cartridge.has_msu1()) msu1.load(); if(cartridge.has_link()) link.load(); @@ -146,6 +148,7 @@ void System::unload() { if(cartridge.has_srtc()) srtc.unload(); if(cartridge.has_sdd1()) sdd1.unload(); if(cartridge.has_spc7110()) spc7110.unload(); + if(cartridge.has_rtc4513()) rtc4513.unload(); if(cartridge.has_obc1()) obc1.unload(); if(cartridge.has_msu1()) msu1.unload(); if(cartridge.has_link()) link.unload(); @@ -172,6 +175,7 @@ void System::power() { if(cartridge.has_srtc()) srtc.power(); if(cartridge.has_sdd1()) sdd1.power(); if(cartridge.has_spc7110()) spc7110.power(); + if(cartridge.has_rtc4513()) rtc4513.power(); if(cartridge.has_obc1()) obc1.power(); if(cartridge.has_msu1()) msu1.power(); if(cartridge.has_link()) link.power(); @@ -198,6 +202,7 @@ void System::reset() { if(cartridge.has_srtc()) srtc.reset(); if(cartridge.has_sdd1()) sdd1.reset(); if(cartridge.has_spc7110()) spc7110.reset(); + if(cartridge.has_rtc4513()) rtc4513.reset(); if(cartridge.has_obc1()) obc1.reset(); if(cartridge.has_msu1()) msu1.reset(); if(cartridge.has_link()) link.reset(); @@ -209,6 +214,7 @@ void System::reset() { if(cartridge.has_hitachidsp()) cpu.coprocessors.append(&hitachidsp); if(cartridge.has_armdsp()) cpu.coprocessors.append(&armdsp); if(cartridge.has_spc7110()) cpu.coprocessors.append(&spc7110); + if(cartridge.has_rtc4513()) cpu.coprocessors.append(&rtc4513); if(cartridge.has_msu1()) cpu.coprocessors.append(&msu1); if(cartridge.has_link()) cpu.coprocessors.append(&link);