Update to v089r03 release.

byuu says:

Substantial improvements to SPC7110 emulation. Added all of the findings
from http://byuu.org/temp/spc7110-mmio.txt that I understood.

I also completely rewrote the RTC. We only had about ~40% of the chip
emulated before. Turns out there's cool stuff like spare RAM, calendar
disable, 12-hour mode, IRQs, IRQ masking, duty cycles, etc. So I went
ahead and emulated all of it. The upper bits on hour+ don't work as
nocash described though, not sure what doc he was reading. The Epson
RTC-4513 manual I have doesn't explain any of the registers.
The new RTC core also ticks seconds based on the emulated clock, and not
on the system clock. This is going to suck for people wanting to keep
the in-game clock synced with their computer, who also abuse fast
forward and save states. Fast forward makes the clock run faster, and
save states will jump the clock to the time it was at when you took the
save state. (It will keep track of the number of seconds between
unloading the game and loading it again, so time passes normally there.)
This is required, however, and how I'm going to rearrange all of the
RTCs for all systems. Any other method can be detected by the game, and
is thus not faithful emulation. To help with this, I'll probably make an
RTC time tool so that you can adjust the time when the emulator isn't
running, but I don't intend to bundle that into bsnes.
New state format bit-packs the RTCRAM values, and it also uses a 64-bit
timestamp. So it's 16 bytes now instead of 20 bytes. S-RTC will drop
from 16 to 12 when it's done.
The RTC busy flag timing needs to be refined with more hardware tests,
there's a slim chance of the game hanging on save at the moment.

The SPC7110 ALU delays are emulated now, too. They may not be perfectly
accurate, but they get the basic gist down.
The only hack that needs to be removed now is the decompression busy
flag. That's ... not going to be fun.

I also redid the mouse emulation. I was polling the mouse position
multiple times per latch. So it should be a bit more precise now,
I hope.
I read it regardless of latch state, dunno if that's good or not.
This commit is contained in:
Tim Allen 2012-05-16 10:27:34 +10:00
parent a1e4c67a05
commit 6ac7b733bd
14 changed files with 771 additions and 573 deletions

View File

@ -3,7 +3,7 @@
namespace Emulator {
static const char Name[] = "bsnes";
static const char Version[] = "089.02";
static const char Version[] = "089.03";
static const char Author[] = "byuu";
static const char License[] = "GPLv3";
}

View File

@ -78,6 +78,8 @@ void MSU1::reset() {
}
uint8 MSU1::mmio_read(unsigned addr) {
cpu.synchronize_coprocessors();
switch(addr & 7) {
case 0:
return (mmio.data_busy << 7)
@ -101,6 +103,8 @@ uint8 MSU1::mmio_read(unsigned addr) {
}
void MSU1::mmio_write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessors();
switch(addr & 7) {
case 0: mmio.data_offset = (mmio.data_offset & 0xffffff00) | (data << 0); break;
case 1: mmio.data_offset = (mmio.data_offset & 0xffff00ff) | (data << 8); break;

83
bsnes/sfc/chip/spc7110/alu.cpp Executable file
View File

@ -0,0 +1,83 @@
#ifdef SPC7110_CPP
void SPC7110::alu_multiply() {
if(r482e & 1) {
//signed 16-bit x 16-bit multiplication
int16 r0 = (int16)(r4824 | r4825 << 8);
int16 r1 = (int16)(r4820 | r4821 << 8);
signed result = r0 * r1;
r4828 = result;
r4829 = result >> 8;
r482a = result >> 16;
r482b = result >> 24;
} else {
//unsigned 16-bit x 16-bit multiplication
uint16 r0 = (uint16)(r4824 | r4825 << 8);
uint16 r1 = (uint16)(r4820 | r4821 << 8);
unsigned result = r0 * r1;
r4828 = result;
r4829 = result >> 8;
r482a = result >> 16;
r482b = result >> 24;
}
r482f &= 0x7f;
}
void SPC7110::alu_divide() {
if(r482e & 1) {
//signed 32-bit x 16-bit division
int32 dividend = (int32)(r4820 | r4821 << 8 | r4822 << 16 | r4823 << 24);
int16 divisor = (int16)(r4826 | r4827 << 8);
int32 quotient;
int16 remainder;
if(divisor) {
quotient = (int32)(dividend / divisor);
remainder = (int32)(dividend % divisor);
} else {
//illegal division by zero
quotient = 0;
remainder = dividend;
}
r4828 = quotient;
r4829 = quotient >> 8;
r482a = quotient >> 16;
r482b = quotient >> 24;
r482c = remainder;
r482d = remainder >> 8;
} else {
//unsigned 32-bit x 16-bit division
uint32 dividend = (uint32)(r4820 | r4821 << 8 | r4822 << 16 | r4823 << 24);
uint16 divisor = (uint16)(r4826 | r4827 << 8);
uint32 quotient;
uint16 remainder;
if(divisor) {
quotient = (uint32)(dividend / divisor);
remainder = (uint16)(dividend % divisor);
} else {
//illegal division by zero
quotient = 0;
remainder = dividend;
}
r4828 = quotient;
r4829 = quotient >> 8;
r482a = quotient >> 16;
r482b = quotient >> 24;
r482c = remainder;
r482d = remainder >> 8;
}
r482f &= 0x7f;
}
#endif

81
bsnes/sfc/chip/spc7110/data.cpp Executable file
View File

@ -0,0 +1,81 @@
#ifdef SPC7110_CPP
uint8 SPC7110::datarom_read(unsigned addr) {
unsigned mask = (1 << (r4834 & 3)) - 1; //8mbit, 16mbit, 32mbit, 64mbit
unsigned range = 0x100000 * (1 + mask);
unsigned offset = addr & 0x7fffff;
//mirroring behavior is non-sensical. assuming a 32mbit data ROM:
//banks 4-7 with mask 0-2 returns 0x00; banks 4-7 with mask 3 mirrors banks 0-3
if(range <= drom_size && offset >= drom_size) return 0x00;
return cartridge.rom.read(drom_base + Bus::mirror(offset, drom_size));
}
unsigned SPC7110::data_offset() { return r4811 | r4812 << 8 | r4813 << 16; }
unsigned SPC7110::data_adjust() { return r4814 | r4815 << 8; }
unsigned SPC7110::data_increment() { return r4816 | r4817 << 8; }
void SPC7110::set_data_offset(unsigned addr) { r4811 = addr; r4812 = addr >> 8; r4813 = addr >> 16; }
void SPC7110::set_data_adjust(unsigned addr) { r4814 = addr; r4815 = addr >> 8; }
void SPC7110::data_port_read_a() {
unsigned offset = data_offset();
unsigned adjust = data_adjust();
if(r4818 & 8) adjust = (int16)adjust;
if(r4818 & 2) offset += adjust;
r4810 = datarom_read(offset);
}
void SPC7110::data_port_read_b() {
unsigned offset = data_offset();
unsigned adjust = data_adjust();
if(r4818 & 8) adjust = (int16)adjust;
r481a = datarom_read(offset + adjust);
}
void SPC7110::data_port_read() {
data_port_read_a();
data_port_read_b();
}
void SPC7110::data_port_increment_a() {
unsigned adjust = data_adjust();
if(r4818 & 8) adjust = (int16)adjust;
if(r4818 & 2) return set_data_adjust(adjust + 1);
unsigned increment = r4818 & 1 ? data_increment() : 1u;
if(r4818 & 4) increment = (int16)increment;
if((r4818 & 16) == 0) set_data_offset(data_offset() + increment);
if((r4818 & 16) != 0) set_data_adjust(adjust + increment);
}
void SPC7110::data_port_increment_b() {
if(r4818 >> 5 != 3) return;
unsigned offset = data_offset();
unsigned adjust = data_adjust();
if(r4818 & 8) adjust = (int16)adjust;
if((r4818 & 16) == 0) set_data_offset(offset + adjust);
if((r4818 & 16) != 0) set_data_adjust(adjust + adjust);
}
void SPC7110::data_port_increment() {
if((r4818 & 2) == 0) return;
if(r4818 & 16) return;
if(r4818 >> 5 == 1) {
unsigned increment = data_adjust() & 0xff;
if(r4818 & 8) increment = (int8)increment;
set_data_offset(data_offset() + increment);
}
if(r4818 >> 5 == 2) {
unsigned increment = data_adjust();
if(r4818 & 8) increment = (int16)increment;
set_data_offset(data_offset() + increment);
}
}
#endif

View File

@ -4,10 +4,10 @@ uint8 SPC7110::Decomp::read() {
if(decomp_buffer_length == 0) {
//decompress at least (decomp_buffer_size / 2) bytes to the buffer
switch(decomp_mode) {
case 0: mode0(false); break;
case 1: mode1(false); break;
case 2: mode2(false); break;
default: return 0x00;
case 0: mode0(false); break;
case 1: mode1(false); break;
case 2: mode2(false); break;
default: return 0x00;
}
}
@ -24,8 +24,7 @@ void SPC7110::Decomp::write(uint8 data) {
}
uint8 SPC7110::Decomp::dataread() {
unsigned addr = Bus::mirror(decomp_offset++, spc7110.drom_size);
return cartridge.rom.read(spc7110.drom_base + addr);
return spc7110.datarom_read(decomp_offset++);
}
void SPC7110::Decomp::init(unsigned mode, unsigned offset, unsigned index) {
@ -43,9 +42,9 @@ void SPC7110::Decomp::init(unsigned mode, unsigned offset, unsigned index) {
}
switch(decomp_mode) {
case 0: mode0(true); break;
case 1: mode1(true); break;
case 2: mode2(true); break;
case 0: mode0(true); break;
case 1: mode1(true); break;
case 2: mode2(true); break;
}
//decompress up to requested output data index
@ -342,106 +341,106 @@ void SPC7110::Decomp::mode2(bool init) {
//
const uint8 SPC7110::Decomp::evolution_table[53][4] = {
//{ prob, nextlps, nextmps, toggle invert },
//{prob, nextlps, nextmps, toggle invert},
{ 0x5a, 1, 1, 1 },
{ 0x25, 6, 2, 0 },
{ 0x11, 8, 3, 0 },
{ 0x08, 10, 4, 0 },
{ 0x03, 12, 5, 0 },
{ 0x01, 15, 5, 0 },
{0x5a, 1, 1, 1},
{0x25, 6, 2, 0},
{0x11, 8, 3, 0},
{0x08, 10, 4, 0},
{0x03, 12, 5, 0},
{0x01, 15, 5, 0},
{ 0x5a, 7, 7, 1 },
{ 0x3f, 19, 8, 0 },
{ 0x2c, 21, 9, 0 },
{ 0x20, 22, 10, 0 },
{ 0x17, 23, 11, 0 },
{ 0x11, 25, 12, 0 },
{ 0x0c, 26, 13, 0 },
{ 0x09, 28, 14, 0 },
{ 0x07, 29, 15, 0 },
{ 0x05, 31, 16, 0 },
{ 0x04, 32, 17, 0 },
{ 0x03, 34, 18, 0 },
{ 0x02, 35, 5, 0 },
{0x5a, 7, 7, 1},
{0x3f, 19, 8, 0},
{0x2c, 21, 9, 0},
{0x20, 22, 10, 0},
{0x17, 23, 11, 0},
{0x11, 25, 12, 0},
{0x0c, 26, 13, 0},
{0x09, 28, 14, 0},
{0x07, 29, 15, 0},
{0x05, 31, 16, 0},
{0x04, 32, 17, 0},
{0x03, 34, 18, 0},
{0x02, 35, 5, 0},
{ 0x5a, 20, 20, 1 },
{ 0x48, 39, 21, 0 },
{ 0x3a, 40, 22, 0 },
{ 0x2e, 42, 23, 0 },
{ 0x26, 44, 24, 0 },
{ 0x1f, 45, 25, 0 },
{ 0x19, 46, 26, 0 },
{ 0x15, 25, 27, 0 },
{ 0x11, 26, 28, 0 },
{ 0x0e, 26, 29, 0 },
{ 0x0b, 27, 30, 0 },
{ 0x09, 28, 31, 0 },
{ 0x08, 29, 32, 0 },
{ 0x07, 30, 33, 0 },
{ 0x05, 31, 34, 0 },
{ 0x04, 33, 35, 0 },
{ 0x04, 33, 36, 0 },
{ 0x03, 34, 37, 0 },
{ 0x02, 35, 38, 0 },
{ 0x02, 36, 5, 0 },
{0x5a, 20, 20, 1},
{0x48, 39, 21, 0},
{0x3a, 40, 22, 0},
{0x2e, 42, 23, 0},
{0x26, 44, 24, 0},
{0x1f, 45, 25, 0},
{0x19, 46, 26, 0},
{0x15, 25, 27, 0},
{0x11, 26, 28, 0},
{0x0e, 26, 29, 0},
{0x0b, 27, 30, 0},
{0x09, 28, 31, 0},
{0x08, 29, 32, 0},
{0x07, 30, 33, 0},
{0x05, 31, 34, 0},
{0x04, 33, 35, 0},
{0x04, 33, 36, 0},
{0x03, 34, 37, 0},
{0x02, 35, 38, 0},
{0x02, 36, 5, 0},
{ 0x58, 39, 40, 1 },
{ 0x4d, 47, 41, 0 },
{ 0x43, 48, 42, 0 },
{ 0x3b, 49, 43, 0 },
{ 0x34, 50, 44, 0 },
{ 0x2e, 51, 45, 0 },
{ 0x29, 44, 46, 0 },
{ 0x25, 45, 24, 0 },
{0x58, 39, 40, 1},
{0x4d, 47, 41, 0},
{0x43, 48, 42, 0},
{0x3b, 49, 43, 0},
{0x34, 50, 44, 0},
{0x2e, 51, 45, 0},
{0x29, 44, 46, 0},
{0x25, 45, 24, 0},
{ 0x56, 47, 48, 1 },
{ 0x4f, 47, 49, 0 },
{ 0x47, 48, 50, 0 },
{ 0x41, 49, 51, 0 },
{ 0x3c, 50, 52, 0 },
{ 0x37, 51, 43, 0 },
{0x56, 47, 48, 1},
{0x4f, 47, 49, 0},
{0x47, 48, 50, 0},
{0x41, 49, 51, 0},
{0x3c, 50, 52, 0},
{0x37, 51, 43, 0},
};
const uint8 SPC7110::Decomp::mode2_context_table[32][2] = {
//{ next 0, next 1 },
//{next 0, next 1},
{ 1, 2 },
{ 1, 2},
{ 3, 8 },
{ 13, 14 },
{ 3, 8},
{13, 14},
{ 15, 16 },
{ 17, 18 },
{ 19, 20 },
{ 21, 22 },
{ 23, 24 },
{ 25, 26 },
{ 25, 26 },
{ 25, 26 },
{ 25, 26 },
{ 25, 26 },
{ 27, 28 },
{ 29, 30 },
{15, 16},
{17, 18},
{19, 20},
{21, 22},
{23, 24},
{25, 26},
{25, 26},
{25, 26},
{25, 26},
{25, 26},
{27, 28},
{29, 30},
{ 31, 31 },
{ 31, 31 },
{ 31, 31 },
{ 31, 31 },
{ 31, 31 },
{ 31, 31 },
{ 31, 31 },
{ 31, 31 },
{ 31, 31 },
{ 31, 31 },
{ 31, 31 },
{ 31, 31 },
{ 31, 31 },
{ 31, 31 },
{ 31, 31 },
{ 31, 31 },
{31, 31},
{31, 31},
{31, 31},
{31, 31},
{31, 31},
{31, 31},
{31, 31},
{31, 31},
{31, 31},
{31, 31},
{31, 31},
{31, 31},
{31, 31},
{31, 31},
{31, 31},
{31, 31},
{ 31, 31 },
{31, 31},
};
uint8 SPC7110::Decomp::probability (unsigned n) { return evolution_table[context[n].index][0]; }

View File

@ -1,5 +1,4 @@
class Decomp {
public:
struct Decomp {
uint8 read();
void init(unsigned mode, unsigned offset, unsigned index);
void reset();

249
bsnes/sfc/chip/spc7110/rtc.cpp Executable file
View File

@ -0,0 +1,249 @@
#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: rtcram[0x5] = data; 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

@ -16,6 +16,8 @@ void SPC7110::Decomp::serialize(serializer &s) {
}
void SPC7110::serialize(serializer &s) {
for(auto &byte : rtcram) s.integer(byte);
s.integer(r4801);
s.integer(r4802);
s.integer(r4803);
@ -28,8 +30,10 @@ void SPC7110::serialize(serializer &s) {
s.integer(r480a);
s.integer(r480b);
s.integer(r480c);
decomp.serialize(s);
s.integer(r4810);
s.integer(r4811);
s.integer(r4812);
s.integer(r4813);
@ -38,7 +42,8 @@ void SPC7110::serialize(serializer &s) {
s.integer(r4816);
s.integer(r4817);
s.integer(r4818);
s.integer(r481x);
s.integer(r481a);
s.integer(r4814_latch);
s.integer(r4815_latch);
@ -59,6 +64,9 @@ void SPC7110::serialize(serializer &s) {
s.integer(r482e);
s.integer(r482f);
s.integer(mul_wait);
s.integer(div_wait);
s.integer(r4830);
s.integer(r4831);
s.integer(r4832);
@ -69,10 +77,11 @@ void SPC7110::serialize(serializer &s) {
s.integer(r4841);
s.integer(r4842);
s.array(rtc);
s.integer(rtc_state);
s.integer(rtc_clocks);
s.integer(rtc_seconds);
s.integer(rtc_mode);
s.integer(rtc_index);
s.integer(rtc_addr);
s.integer(rtc_wait);
}
#endif

View File

@ -3,19 +3,50 @@
#define SPC7110_CPP
namespace SuperFamicom {
#include "decomp.cpp"
#include "data.cpp"
#include "alu.cpp"
#include "rtc.cpp"
#include "serialization.cpp"
SPC7110 spc7110;
#include "serialization.cpp"
#include "decomp.cpp"
void SPC7110::Enter() { spc7110.enter(); }
const unsigned SPC7110::months[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
void SPC7110::enter() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
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 &= 0x7f; }
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();
}
}
void SPC7110::init() {
}
void SPC7110::load() {
for(unsigned n = 0; n < 20; n++) rtc[n] = 0xff;
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() {
@ -25,6 +56,8 @@ void SPC7110::power() {
}
void SPC7110::reset() {
create(SPC7110::Enter, 32768 * 128);
r4801 = 0x00;
r4802 = 0x00;
r4803 = 0x00;
@ -40,6 +73,7 @@ void SPC7110::reset() {
decomp.reset();
r4810 = 0x00;
r4811 = 0x00;
r4812 = 0x00;
r4813 = 0x00;
@ -48,8 +82,8 @@ void SPC7110::reset() {
r4816 = 0x00;
r4817 = 0x00;
r4818 = 0x00;
r481a = 0x00;
r481x = 0x00;
r4814_latch = false;
r4815_latch = false;
@ -70,6 +104,9 @@ void SPC7110::reset() {
r482e = 0x00;
r482f = 0x00;
mul_wait = 0;
div_wait = 0;
r4830 = 0x00;
r4831 = 0x00;
r4832 = 0x01;
@ -80,116 +117,15 @@ void SPC7110::reset() {
r4841 = 0x00;
r4842 = 0x00;
if(cartridge.has_spc7110rtc()) {
rtc_state = RTCS_Inactive;
rtc_mode = RTCM_Linear;
rtc_index = 0;
}
}
unsigned SPC7110::datarom_addr(unsigned addr) {
addr = Bus::mirror(addr, drom_size);
return drom_base + addr;
}
unsigned SPC7110::data_pointer() { return r4811 + (r4812 << 8) + (r4813 << 16); }
unsigned SPC7110::data_adjust() { return r4814 + (r4815 << 8); }
unsigned SPC7110::data_increment() { return r4816 + (r4817 << 8); }
void SPC7110::set_data_pointer(unsigned addr) { r4811 = addr; r4812 = addr >> 8; r4813 = addr >> 16; }
void SPC7110::set_data_adjust(unsigned addr) { r4814 = addr; r4815 = addr >> 8; }
void SPC7110::update_time(int offset) {
time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24);
time_t current_time = time(0) - offset;
//sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic.
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
//accounting for overflow at the cost of 1-bit precision (to catch underflow). this will allow
//rtc[] timestamp to remain valid for up to ~34 years from the last update, even if
//time_t overflows. calculation should be valid regardless of number representation, time_t size,
//or whether time_t is signed or unsigned.
time_t diff
= (current_time >= rtc_time)
? (current_time - rtc_time)
: (std::numeric_limits<time_t>::max() - rtc_time + current_time + 1); //compensate for overflow
if(diff > std::numeric_limits<time_t>::max() / 2) diff = 0; //compensate for underflow
bool update = true;
if(rtc[13] & 1) update = false; //do not update if CR0 timer disable flag is set
if(rtc[15] & 3) update = false; //do not update if CR2 timer disable flags are set
if(diff > 0 && update == true) {
unsigned second = rtc[ 0] + rtc[ 1] * 10;
unsigned minute = rtc[ 2] + rtc[ 3] * 10;
unsigned hour = rtc[ 4] + rtc[ 5] * 10;
unsigned day = rtc[ 6] + rtc[ 7] * 10;
unsigned month = rtc[ 8] + rtc[ 9] * 10;
unsigned year = rtc[10] + rtc[11] * 10;
unsigned weekday = rtc[12];
day--;
month--;
year += (year >= 90) ? 1900 : 2000; //range = 1990-2089
second += diff;
while(second >= 60) {
second -= 60;
minute++;
if(minute < 60) continue;
minute = 0;
hour++;
if(hour < 24) continue;
hour = 0;
day++;
weekday = (weekday + 1) % 7;
unsigned days = months[month % 12];
if(days == 28) {
bool leapyear = false;
if((year % 4) == 0) {
leapyear = true;
if((year % 100) == 0 && (year % 400) != 0) leapyear = false;
}
if(leapyear) days++;
}
if(day < days) continue;
day = 0;
month++;
if(month < 12) continue;
month = 0;
year++;
}
day++;
month++;
year %= 100;
rtc[ 0] = second % 10;
rtc[ 1] = second / 10;
rtc[ 2] = minute % 10;
rtc[ 3] = minute / 10;
rtc[ 4] = hour % 10;
rtc[ 5] = hour / 10;
rtc[ 6] = day % 10;
rtc[ 7] = day / 10;
rtc[ 8] = month % 10;
rtc[ 9] = month / 10;
rtc[10] = year % 10;
rtc[11] = (year / 10) % 10;
rtc[12] = weekday % 7;
}
rtc[16] = current_time >> 0;
rtc[17] = current_time >> 8;
rtc[18] = current_time >> 16;
rtc[19] = current_time >> 24;
rtc_clocks = 0;
rtc_seconds = 0;
rtc_mode = 0;
rtc_addr = 0;
rtc_wait = 0;
}
uint8 SPC7110::mmio_read(unsigned addr) {
cpu.synchronize_coprocessors();
addr &= 0xffff;
switch(addr) {
@ -199,7 +135,7 @@ uint8 SPC7110::mmio_read(unsigned addr) {
//==================
case 0x4800: {
uint16 counter = (r4809 + (r480a << 8));
uint16 counter = r4809 | r480a << 8;
counter--;
r4809 = counter;
r480a = counter >> 8;
@ -212,7 +148,7 @@ uint8 SPC7110::mmio_read(unsigned addr) {
case 0x4805: return r4805;
case 0x4806: return r4806;
case 0x4807: return r4807;
case 0x4808: return r4808;
case 0x4808: return 0x00;
case 0x4809: return r4809;
case 0x480a: return r480a;
case 0x480b: return r480b;
@ -227,30 +163,9 @@ uint8 SPC7110::mmio_read(unsigned addr) {
//==============
case 0x4810: {
if(r481x != 0x07) return 0x00;
unsigned addr = data_pointer();
unsigned adjust = data_adjust();
if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend
unsigned adjustaddr = addr;
if(r4818 & 2) {
adjustaddr += adjust;
set_data_adjust(adjust + 1);
}
uint8 data = cartridge.rom.read(datarom_addr(adjustaddr));
if(!(r4818 & 2)) {
unsigned increment = (r4818 & 1) ? data_increment() : 1;
if(r4818 & 4) increment = (int16)increment; //16-bit sign extend
if((r4818 & 16) == 0) {
set_data_pointer(addr + increment);
} else {
set_data_adjust(adjust + increment);
}
}
uint8 data = r4810;
data_port_increment_a();
data_port_read_a();
return data;
}
case 0x4811: return r4811;
@ -262,21 +177,9 @@ uint8 SPC7110::mmio_read(unsigned addr) {
case 0x4817: return r4817;
case 0x4818: return r4818;
case 0x481a: {
if(r481x != 0x07) return 0x00;
unsigned addr = data_pointer();
unsigned adjust = data_adjust();
if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend
uint8 data = cartridge.rom.read(datarom_addr(addr + adjust));
if((r4818 & 0x60) == 0x60) {
if((r4818 & 16) == 0) {
set_data_pointer(addr + adjust);
} else {
set_data_adjust(adjust + adjust);
}
}
uint8 data = r481a;
data_port_increment_b();
data_port_read_b();
return data;
}
@ -299,11 +202,7 @@ uint8 SPC7110::mmio_read(unsigned addr) {
case 0x482c: return r482c;
case 0x482d: return r482d;
case 0x482e: return r482e;
case 0x482f: {
uint8 status = r482f;
r482f &= 0x7f;
return status;
}
case 0x482f: return r482f;
//===================
//memory mapping unit
@ -321,18 +220,13 @@ uint8 SPC7110::mmio_read(unsigned addr) {
case 0x4840: return r4840;
case 0x4841: {
if(rtc_state == RTCS_Inactive || rtc_state == RTCS_ModeSelect) return 0x00;
r4842 = 0x80;
uint8 data = rtc[rtc_index];
rtc_index = (rtc_index + 1) & 15;
return data;
}
case 0x4842: {
uint8 status = r4842;
r4842 &= 0x7f;
return status;
if((r4840 & 1) == 0) return 0x00; //RTC disabled?
if(rtc_mode != 2) return 0x00;
r4842 |= 0x80;
rtc_wait = rtc_delay;
return rtc_read(rtc_addr++);
}
case 0x4842: return r4842;
}
@ -340,6 +234,7 @@ uint8 SPC7110::mmio_read(unsigned addr) {
}
void SPC7110::mmio_write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessors();
addr &= 0xffff;
switch(addr) {
@ -350,79 +245,47 @@ void SPC7110::mmio_write(unsigned addr, uint8 data) {
case 0x4801: r4801 = data; break;
case 0x4802: r4802 = data; break;
case 0x4803: r4803 = data; break;
case 0x4803: r4803 = data & 0x7f; break;
case 0x4804: r4804 = data; break;
case 0x4805: r4805 = data; break;
case 0x4806: {
r4806 = data;
case 0x4806: r4806 = data; {
unsigned table = r4801 | r4802 << 8 | r4803 << 16;
unsigned index = r4804 << 2;
unsigned addr = table + index;
unsigned length = r4809 | r480a << 8;
unsigned mode = datarom_read(addr + 0);
unsigned offset = datarom_read(addr + 1) << 16
| datarom_read(addr + 2) << 8
| datarom_read(addr + 3) << 0;
unsigned table = (r4801 + (r4802 << 8) + (r4803 << 16));
unsigned index = (r4804 << 2);
unsigned length = (r4809 + (r480a << 8));
unsigned addr = datarom_addr(table + index);
unsigned mode = (cartridge.rom.read(addr + 0));
unsigned offset = (cartridge.rom.read(addr + 1) << 16)
+ (cartridge.rom.read(addr + 2) << 8)
+ (cartridge.rom.read(addr + 3) << 0);
decomp.init(mode, offset, (r4805 + (r4806 << 8)) << mode);
decomp.init(mode, offset, (r4805 | r4806 << 8) << mode);
r480c = 0x80;
} break;
case 0x4807: r4807 = data; break;
case 0x4808: r4808 = data; break;
case 0x4808: break;
case 0x4809: r4809 = data; break;
case 0x480a: r480a = data; break;
case 0x480b: r480b = data; break;
case 0x480b: r480b = data & 0x03; break;
//==============
//data port unit
//==============
case 0x4811: r4811 = data; r481x |= 0x01; break;
case 0x4812: r4812 = data; r481x |= 0x02; break;
case 0x4813: r4813 = data; r481x |= 0x04; break;
case 0x4814: {
r4814 = data;
r4814_latch = true;
if(!r4815_latch) break;
if(!(r4818 & 2)) break;
if(r4818 & 0x10) break;
if((r4818 & 0x60) == 0x20) {
unsigned increment = data_adjust() & 0xff;
if(r4818 & 8) increment = (int8)increment; //8-bit sign extend
set_data_pointer(data_pointer() + increment);
} else if((r4818 & 0x60) == 0x40) {
unsigned increment = data_adjust();
if(r4818 & 8) increment = (int16)increment; //16-bit sign extend
set_data_pointer(data_pointer() + increment);
}
} break;
case 0x4811: r4811 = data; break;
case 0x4812: r4812 = data; break;
case 0x4813: r4813 = data & 0x7f; data_port_read(); break;
case 0x4814:
case 0x4815: {
r4815 = data;
r4815_latch = true;
if(!r4814_latch) break;
if(!(r4818 & 2)) break;
if(r4818 & 0x10) break;
if((r4818 & 0x60) == 0x20) {
unsigned increment = data_adjust() & 0xff;
if(r4818 & 8) increment = (int8)increment; //8-bit sign extend
set_data_pointer(data_pointer() + increment);
} else if((r4818 & 0x60) == 0x40) {
unsigned increment = data_adjust();
if(r4818 & 8) increment = (int16)increment; //16-bit sign extend
set_data_pointer(data_pointer() + increment);
}
if((addr & 1) == 0) r4814 = data, r4814_latch = true;
if((addr & 1) == 1) r4815 = data, r4815_latch = true;
if(r4814_latch && r4815_latch) data_port_increment();
} break;
case 0x4816: r4816 = data; break;
case 0x4817: r4817 = data; break;
case 0x4818: {
if(r481x != 0x07) break;
r4818 = data;
case 0x4818: r4818 = data & 0x7f; {
r4814_latch = r4815_latch = false;
data_port_read();
} break;
//=========
@ -434,99 +297,10 @@ void SPC7110::mmio_write(unsigned addr, uint8 data) {
case 0x4822: r4822 = data; break;
case 0x4823: r4823 = data; break;
case 0x4824: r4824 = data; break;
case 0x4825: {
r4825 = data;
if(r482e & 1) {
//signed 16-bit x 16-bit multiplication
int16 r0 = (int16)(r4824 + (r4825 << 8));
int16 r1 = (int16)(r4820 + (r4821 << 8));
signed result = r0 * r1;
r4828 = result;
r4829 = result >> 8;
r482a = result >> 16;
r482b = result >> 24;
} else {
//unsigned 16-bit x 16-bit multiplication
uint16 r0 = (uint16)(r4824 + (r4825 << 8));
uint16 r1 = (uint16)(r4820 + (r4821 << 8));
unsigned result = r0 * r1;
r4828 = result;
r4829 = result >> 8;
r482a = result >> 16;
r482b = result >> 24;
}
r482f = 0x80;
} break;
case 0x4825: r4825 = data; r482f |= 0x81; mul_wait = mul_delay; break;
case 0x4826: r4826 = data; break;
case 0x4827: {
r4827 = data;
if(r482e & 1) {
//signed 32-bit x 16-bit division
int32 dividend = (int32)(r4820 + (r4821 << 8) + (r4822 << 16) + (r4823 << 24));
int16 divisor = (int16)(r4826 + (r4827 << 8));
int32 quotient;
int16 remainder;
if(divisor) {
quotient = (int32)(dividend / divisor);
remainder = (int32)(dividend % divisor);
} else {
//illegal division by zero
quotient = 0;
remainder = dividend & 0xffff;
}
r4828 = quotient;
r4829 = quotient >> 8;
r482a = quotient >> 16;
r482b = quotient >> 24;
r482c = remainder;
r482d = remainder >> 8;
} else {
//unsigned 32-bit x 16-bit division
uint32 dividend = (uint32)(r4820 + (r4821 << 8) + (r4822 << 16) + (r4823 << 24));
uint16 divisor = (uint16)(r4826 + (r4827 << 8));
uint32 quotient;
uint16 remainder;
if(divisor) {
quotient = (uint32)(dividend / divisor);
remainder = (uint16)(dividend % divisor);
} else {
//illegal division by zero
quotient = 0;
remainder = dividend & 0xffff;
}
r4828 = quotient;
r4829 = quotient >> 8;
r482a = quotient >> 16;
r482b = quotient >> 24;
r482c = remainder;
r482d = remainder >> 8;
}
r482f = 0x80;
} break;
case 0x482e: {
//reset math unit
r4820 = r4821 = r4822 = r4823 = 0;
r4824 = r4825 = r4826 = r4827 = 0;
r4828 = r4829 = r482a = r482b = 0;
r482c = r482d = 0;
r482e = data;
} break;
case 0x4827: r4827 = data; r482f |= 0x80; div_wait = div_delay; break;
case 0x482e: r482e = data & 0x01; break;
//===================
//memory mapping unit
@ -542,81 +316,36 @@ void SPC7110::mmio_write(unsigned addr, uint8 data) {
//real-time clock unit
//====================
case 0x4840: {
r4840 = data;
if(!(r4840 & 1)) {
//disable RTC
rtc_state = RTCS_Inactive;
update_time();
} else {
//enable RTC
r4842 = 0x80;
rtc_state = RTCS_ModeSelect;
}
case 0x4840: r4840 = data & 0x03; {
if((r4840 & 1) == 0) rtc_reset();
r4842 |= 0x80;
rtc_wait = rtc_delay;
} break;
case 0x4841: {
r4841 = data;
if((r4840 & 1) == 0) break; //RTC disabled?
switch(rtc_state) {
case RTCS_ModeSelect: {
if(data == RTCM_Linear || data == RTCM_Indexed) {
r4842 = 0x80;
rtc_state = RTCS_IndexSelect;
rtc_mode = (RTC_Mode)data;
rtc_index = 0;
}
} break;
r4842 |= 0x80;
rtc_wait = rtc_delay;
case RTCS_IndexSelect: {
r4842 = 0x80;
rtc_index = data & 15;
if(rtc_mode == RTCM_Linear) rtc_state = RTCS_Write;
} break;
if(rtc_mode == 0) {
r4841 = data & 0xf;
rtc_mode = 1;
rtc_addr = 0;
break;
}
case RTCS_Write: {
r4842 = 0x80;
if(rtc_mode == 1) {
rtc_mode = r4841 == 0x0c ? 2 : r4841 == 0x3 ? 3 : 0;
rtc_addr = data & 0xf;
break;
}
//control register 0
if(rtc_index == 13) {
//increment second counter
if(data & 2) update_time(+1);
//round minute counter
if(data & 8) {
update_time();
unsigned second = rtc[ 0] + rtc[ 1] * 10;
//clear seconds
rtc[0] = 0;
rtc[1] = 0;
if(second >= 30) update_time(+60);
}
}
//control register 2
if(rtc_index == 15) {
//disable timer and clear second counter
if((data & 1) && !(rtc[15] & 1)) {
update_time();
//clear seconds
rtc[0] = 0;
rtc[1] = 0;
}
//disable timer
if((data & 2) && !(rtc[15] & 2)) {
update_time();
}
}
rtc[rtc_index] = data & 15;
rtc_index = (rtc_index + 1) & 15;
} break;
} //switch(rtc_state)
} break;
if(rtc_mode == 3) {
rtc_write(rtc_addr++, data);
break;
}
}
}
}
@ -640,16 +369,17 @@ void SPC7110::dcu_write(unsigned, uint8) {
//===============
uint8 SPC7110::mcurom_read(unsigned addr) {
unsigned bank = 0;
unsigned mask = (1 << (r4834 & 3)) - 1; //8mbit, 16mbit, 32mbit, 64mbit DROM
if((addr & 0x708000) == 0x008000 //$00-0f|80-8f:8000-ffff
|| (addr & 0xf00000) == 0xc00000 // $c0-cf:0000-ffff
) {
addr &= 0x0fffff;
if(true) { //8mbit PROM
if(prom_size) { //8mbit PROM
return cartridge.rom.read(prom_base + bus.mirror(0x000000 + addr, prom_size));
}
return mcurom_read_data(r4830 & 7, addr);
addr |= 0x100000 * (r4830 & 7);
return datarom_read(addr);
}
if((addr & 0x708000) == 0x108000 //$10-1f|90-9f:8000-ffff
@ -659,38 +389,29 @@ uint8 SPC7110::mcurom_read(unsigned addr) {
if(r4834 & 4) { //16mbit PROM
return cartridge.rom.read(prom_base + bus.mirror(0x100000 + addr, prom_size));
}
return mcurom_read_data(r4831 & 7, addr);
addr |= 0x100000 * (r4831 & 7);
return datarom_read(addr);
}
if((addr & 0x708000) == 0x208000 //$20-2f|a0-af:8000-ffff
|| (addr & 0xf00000) == 0xe00000 // $e0-ef:0000-ffff
) {
addr &= 0x0fffff;
return mcurom_read_data(r4832 & 7, addr);
addr |= 0x100000 * (r4832 & 7);
return datarom_read(addr);
}
if((addr & 0x708000) == 0x308000 //$30-3f|b0-bf:8000-ffff
|| (addr & 0xf00000) == 0xf00000 // $f0-ff:0000-ffff
) {
addr &= 0x0fffff;
return mcurom_read_data(r4833 & 7, addr);
addr |= 0x100000 * (r4833 & 7);
return datarom_read(addr);
}
return cpu.regs.mdr;
}
uint8 SPC7110::mcurom_read_data(unsigned bank, unsigned addr) {
unsigned mask = (1 << (r4834 & 3)) - 1; //8mbit, 16mbit, 32mbit, 64mbit DROM
unsigned range = 0x100000 * (1 + mask);
unsigned offset = 0x100000 * (bank & mask);
//mirroring behavior is non-sensical. assuming a 32mbit data ROM:
//banks 4-7 with mask 0-2 returns 0x00; banks 4-7 with mask 3 mirrors banks 0-3
if(range <= drom_size && offset >= drom_size) return 0x00;
return cartridge.rom.read(drom_base + bus.mirror(offset + addr, drom_size));
}
void SPC7110::mcurom_write(unsigned addr, uint8 data) {
}

View File

@ -1,28 +1,22 @@
//SPC7110 emulator - version 0.05 (2011-06-27)
//Copyright (c) 2008-2011, byuu and neviksti
struct SPC7110 {
uint8 rtc[20];
struct SPC7110 : Coprocessor {
unsigned prom_base, prom_size; //program ROM
unsigned drom_base, drom_size; //data ROM
uint4 rtcram[16];
enum : unsigned {
mul_delay = 6,
div_delay = 8,
rtc_delay = 24,
};
static void Enter();
void enter();
void init();
void load();
void unload();
void power();
void reset();
unsigned datarom_addr(unsigned addr);
unsigned data_pointer();
unsigned data_adjust();
unsigned data_increment();
void set_data_pointer(unsigned addr);
void set_data_adjust(unsigned addr);
void update_time(int offset = 0);
time_t create_time();
uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data);
@ -30,19 +24,54 @@ struct SPC7110 {
void dcu_write(unsigned, uint8);
uint8 mcurom_read(unsigned addr);
uint8 mcurom_read_data(unsigned bank, unsigned addr);
void mcurom_write(unsigned addr, uint8 data);
uint8 mcuram_read(unsigned addr);
void mcuram_write(unsigned addr, uint8 data);
//spc7110decomp
void decomp_init();
uint8 decomp_read();
void serialize(serializer&);
SPC7110();
//data.cpp
uint8 datarom_read(unsigned addr);
unsigned data_offset();
unsigned data_adjust();
unsigned data_increment();
void set_data_offset(unsigned addr);
void set_data_adjust(unsigned addr);
void data_port_read_a();
void data_port_read_b();
void data_port_read();
void data_port_increment_a();
void data_port_increment_b();
void data_port_increment();
//alu.cpp
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
@ -53,7 +82,7 @@ private:
uint8 r4804; //compression table index
uint8 r4805; //decompression buffer index low
uint8 r4806; //decompression buffer index high
uint8 r4807; //???
uint8 r4807; //??? (used when $480b.d0 = 1)
uint8 r4808; //???
uint8 r4809; //compression length low
uint8 r480a; //compression length high
@ -66,16 +95,16 @@ private:
//==============
//data port unit
//==============
uint8 r4811; //data pointer low
uint8 r4812; //data pointer high
uint8 r4813; //data pointer bank
uint8 r4810; //data port A
uint8 r4811; //data offset low
uint8 r4812; //data offset high
uint8 r4813; //data offset bank
uint8 r4814; //data adjust low
uint8 r4815; //data adjust high
uint8 r4816; //data increment low
uint8 r4817; //data increment high
uint8 r4818; //data port control register
uint8 r481x;
uint8 r481a; //data port B
bool r4814_latch;
bool r4815_latch;
@ -97,9 +126,12 @@ private:
uint8 r482b; //32-bit product B3, 32-bit quotient B3
uint8 r482c; //16-bit remainder B0
uint8 r482d; //16-bit remainder B1
uint8 r482e; //math control register
uint8 r482e; //math sign extend mode
uint8 r482f; //math status
unsigned mul_wait;
unsigned div_wait;
//===================
//memory mapping unit
//===================
@ -112,17 +144,15 @@ private:
//====================
//real-time clock unit
//====================
uint8 r4840; //RTC latch
uint8 r4841; //RTC index/data port
uint8 r4840; //RTC enable
uint8 r4841; //RTC data port
uint8 r4842; //RTC status
enum RTC_State { RTCS_Inactive, RTCS_ModeSelect, RTCS_IndexSelect, RTCS_Write };
enum RTC_Mode { RTCM_Linear = 0x03, RTCM_Indexed = 0x0c };
unsigned rtc_state;
unsigned rtc_mode;
unsigned rtc_index;
static const unsigned months[12];
uint22 rtc_clocks;
unsigned rtc_seconds;
uint2 rtc_mode; //0 = idle, 1 = seek, 2 = read, 3 = write
uint4 rtc_addr;
unsigned rtc_wait;
};
extern SPC7110 spc7110;

View File

@ -3,18 +3,6 @@
uint2 Mouse::data() {
if(counter >= 32) return 1;
int position_x = interface->inputPoll(port, (unsigned)Input::Device::Mouse, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
int position_y = interface->inputPoll(port, (unsigned)Input::Device::Mouse, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
bool direction_x = position_x < 0; //0 = right, 1 = left
bool direction_y = position_y < 0; //0 = down, 1 = up
if(position_x < 0) position_x = -position_x; //abs(position_x)
if(position_y < 0) position_y = -position_y; //abs(position_y)
position_x = min(127, position_x); //range = 0 - 127
position_y = min(127, position_y);
switch(counter++) { default:
case 0: return 0;
case 1: return 0;
@ -25,8 +13,8 @@ uint2 Mouse::data() {
case 6: return 0;
case 7: return 0;
case 8: return interface->inputPoll(port, (unsigned)Input::Device::Mouse, (unsigned)Input::MouseID::Right);
case 9: return interface->inputPoll(port, (unsigned)Input::Device::Mouse, (unsigned)Input::MouseID::Left);
case 8: return input.r;
case 9: return input.l;
case 10: return 0; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused)
case 11: return 0; // ||
@ -35,23 +23,23 @@ uint2 Mouse::data() {
case 14: return 0; // ||
case 15: return 1; // ||
case 16: return (direction_y);
case 17: return (position_y >> 6) & 1;
case 18: return (position_y >> 5) & 1;
case 19: return (position_y >> 4) & 1;
case 20: return (position_y >> 3) & 1;
case 21: return (position_y >> 2) & 1;
case 22: return (position_y >> 1) & 1;
case 23: return (position_y >> 0) & 1;
case 16: return input.dy;
case 17: return (input.y >> 6) & 1;
case 18: return (input.y >> 5) & 1;
case 19: return (input.y >> 4) & 1;
case 20: return (input.y >> 3) & 1;
case 21: return (input.y >> 2) & 1;
case 22: return (input.y >> 1) & 1;
case 23: return (input.y >> 0) & 1;
case 24: return (direction_x);
case 25: return (position_x >> 6) & 1;
case 26: return (position_x >> 5) & 1;
case 27: return (position_x >> 4) & 1;
case 28: return (position_x >> 3) & 1;
case 29: return (position_x >> 2) & 1;
case 30: return (position_x >> 1) & 1;
case 31: return (position_x >> 0) & 1;
case 24: return input.dx;
case 25: return (input.x >> 6) & 1;
case 26: return (input.x >> 5) & 1;
case 27: return (input.x >> 4) & 1;
case 28: return (input.x >> 3) & 1;
case 29: return (input.x >> 2) & 1;
case 30: return (input.x >> 1) & 1;
case 31: return (input.x >> 0) & 1;
}
}
@ -59,11 +47,32 @@ void Mouse::latch(bool data) {
if(latched == data) return;
latched = data;
counter = 0;
input.x = interface->inputPoll(port, (unsigned)Input::Device::Mouse, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
input.y = interface->inputPoll(port, (unsigned)Input::Device::Mouse, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
input.l = interface->inputPoll(port, (unsigned)Input::Device::Mouse, (unsigned)Input::MouseID::Left );
input.r = interface->inputPoll(port, (unsigned)Input::Device::Mouse, (unsigned)Input::MouseID::Right);
input.dx = input.x < 0; //0 = right, 1 = left
input.dy = input.y < 0; //0 = down, 1 = up
if(input.x < 0) input.x = -input.x; //abs(position_x)
if(input.y < 0) input.y = -input.y; //abs(position_y)
input.x = min(127, input.x);
input.y = min(127, input.y);
}
Mouse::Mouse(bool port) : Controller(port) {
latched = 0;
counter = 0;
input.x = 0;
input.y = 0;
input.dx = 0;
input.dy = 0;
input.l = 0;
input.r = 0;
}
#endif

View File

@ -6,4 +6,13 @@ struct Mouse : Controller {
private:
bool latched;
unsigned counter;
struct MouseInput {
signed x; //x-coordinate
signed y; //y-coordinate
bool dx; //x-direction
bool dy; //y-direction
bool l; //left button
bool r; //right button
} input;
};

View File

@ -110,7 +110,9 @@ void Interface::load(unsigned id, const stream &stream, const string &markup) {
}
if(id == ID::SPC7110RTC) {
stream.read(spc7110.rtc, min(stream.size(), sizeof srtc.rtc));
uint8 data[16] = {0};
stream.read(data, min(stream.size(), sizeof data));
spc7110.rtcram_load(data);
}
if(id == ID::BsxRAM) {
@ -148,7 +150,9 @@ void Interface::save(unsigned id, const stream &stream) {
}
if(id == ID::SPC7110RTC) {
stream.write(spc7110.rtc, sizeof srtc.rtc);
uint8 data[16];
spc7110.rtcram_save(data);
stream.write(data, sizeof data);
}
if(id == ID::BsxRAM) {

View File

@ -208,6 +208,7 @@ void System::reset() {
if(cartridge.has_necdsp()) cpu.coprocessors.append(&necdsp);
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_msu1()) cpu.coprocessors.append(&msu1);
if(cartridge.has_link()) cpu.coprocessors.append(&link);