From bd61432322214e2f09fa3a0ef55d8bff0d925cab Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 21 May 2012 20:56:48 +1000 Subject: [PATCH] Update to v089r05 release. byuu says: I split the RTC-4513 code from the SPC7110 code (and obviously in the XML mapping as well), since they are separate chips on the FEoEZ PCB. In this way, you can use just the RTC-4513 in homebrew now if you want. It's a bit nicer than the Sharp RTC from Dai Kaijuu Monogatari II. This was needed anyway, it has an internal oscillator that's not divisible by the SNES clock used by the SPC7110; and both the RTC and decompression code need to be running their own threads anyway. In the process, I rewrote the way variables are stored to use named integers rather than a block of memory. Makes the code a lot easier on the eyes, and more importantly, will make emulating bad BCD values a whole lot easier. --- bsnes/emulator/emulator.hpp | 2 +- bsnes/sfc/Makefile | 3 +- bsnes/sfc/cartridge/cartridge.cpp | 2 +- bsnes/sfc/cartridge/cartridge.hpp | 3 +- bsnes/sfc/cartridge/markup.cpp | 22 +- bsnes/sfc/chip/chip.hpp | 1 + bsnes/sfc/chip/rtc4513/memory.cpp | 162 +++++++++++++++ bsnes/sfc/chip/rtc4513/rtc4513.cpp | 131 ++++++++++++ bsnes/sfc/chip/rtc4513/rtc4513.hpp | 105 ++++++++++ bsnes/sfc/chip/rtc4513/serialization.cpp | 60 ++++++ bsnes/sfc/chip/rtc4513/time.cpp | 73 +++++++ bsnes/sfc/chip/spc7110/rtc.cpp | 252 ----------------------- bsnes/sfc/chip/spc7110/serialization.cpp | 13 -- bsnes/sfc/chip/spc7110/spc7110.cpp | 82 -------- bsnes/sfc/chip/spc7110/spc7110.hpp | 33 +-- bsnes/sfc/interface/interface.cpp | 10 +- bsnes/sfc/interface/interface.hpp | 2 +- bsnes/sfc/system/serialization.cpp | 1 + bsnes/sfc/system/system.cpp | 6 + 19 files changed, 565 insertions(+), 398 deletions(-) create mode 100755 bsnes/sfc/chip/rtc4513/memory.cpp create mode 100755 bsnes/sfc/chip/rtc4513/rtc4513.cpp create mode 100755 bsnes/sfc/chip/rtc4513/rtc4513.hpp create mode 100755 bsnes/sfc/chip/rtc4513/serialization.cpp create mode 100755 bsnes/sfc/chip/rtc4513/time.cpp delete mode 100755 bsnes/sfc/chip/spc7110/rtc.cpp diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index 1af327b7..1a50ae44 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 b48f3466..e0274807 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 75095656..d9ecb2e2 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 d81f0ab8..3633b5d6 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 cec6d075..3cd5e3d6 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 3081b91d..bded8ed6 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 00000000..db18b62a --- /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 00000000..b5b130a1 --- /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 00000000..07dc69da --- /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 00000000..1b6cd4e9 --- /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 00000000..6ba43985 --- /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 5386137b..00000000 --- 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 3aee8f6e..180959a6 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 8872ec89..50fecbe6 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 584ff432..33ff6985 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 f9944c1e..efabd643 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 aef7933f..5d02f97f 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 ebccf396..6c4087db 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 71cee1e8..898251e8 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);