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.
This commit is contained in:
Tim Allen 2012-05-21 20:56:48 +10:00
parent 0611fefefa
commit bd61432322
19 changed files with 565 additions and 398 deletions

View File

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

View File

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

View File

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

View File

@ -35,7 +35,7 @@ struct Cartridge : property<Cartridge> {
readonly<bool> has_srtc;
readonly<bool> has_sdd1;
readonly<bool> has_spc7110;
readonly<bool> has_spc7110rtc;
readonly<bool> has_rtc4513;
readonly<bool> has_obc1;
readonly<bool> has_msu1;
readonly<bool> 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&);

View File

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

View File

@ -14,6 +14,7 @@ struct Coprocessor : Thread {
#include <sfc/chip/srtc/srtc.hpp>
#include <sfc/chip/sdd1/sdd1.hpp>
#include <sfc/chip/spc7110/spc7110.hpp>
#include <sfc/chip/rtc4513/rtc4513.hpp>
#include <sfc/chip/obc1/obc1.hpp>
#include <sfc/chip/sufamiturbo/sufamiturbo.hpp>
#include <sfc/chip/msu1/msu1.hpp>

162
bsnes/sfc/chip/rtc4513/memory.cpp Executable file
View File

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

View File

@ -0,0 +1,131 @@
#include <sfc/sfc.hpp>
#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;
}
}
}
}

View File

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

View File

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

73
bsnes/sfc/chip/rtc4513/time.cpp Executable file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,7 +17,7 @@ struct ID {
RAM,
NecDSPRAM,
RTC,
SPC7110RTC,
RTC4513,
BsxRAM,
BsxPSRAM,
SuperGameBoyRAM,

View File

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

View File

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