diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index d96707020..936296026 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.02"; + static const char Version[] = "089.03"; static const char Author[] = "byuu"; static const char License[] = "GPLv3"; } diff --git a/bsnes/sfc/chip/msu1/msu1.cpp b/bsnes/sfc/chip/msu1/msu1.cpp index 0c0b46161..606b5a962 100755 --- a/bsnes/sfc/chip/msu1/msu1.cpp +++ b/bsnes/sfc/chip/msu1/msu1.cpp @@ -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; diff --git a/bsnes/sfc/chip/spc7110/alu.cpp b/bsnes/sfc/chip/spc7110/alu.cpp new file mode 100755 index 000000000..99f351f8b --- /dev/null +++ b/bsnes/sfc/chip/spc7110/alu.cpp @@ -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 diff --git a/bsnes/sfc/chip/spc7110/data.cpp b/bsnes/sfc/chip/spc7110/data.cpp new file mode 100755 index 000000000..569d7def3 --- /dev/null +++ b/bsnes/sfc/chip/spc7110/data.cpp @@ -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 diff --git a/bsnes/sfc/chip/spc7110/decomp.cpp b/bsnes/sfc/chip/spc7110/decomp.cpp index 6ad017088..c04c2f09f 100755 --- a/bsnes/sfc/chip/spc7110/decomp.cpp +++ b/bsnes/sfc/chip/spc7110/decomp.cpp @@ -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]; } diff --git a/bsnes/sfc/chip/spc7110/decomp.hpp b/bsnes/sfc/chip/spc7110/decomp.hpp index c11c6ad47..d12553d9f 100755 --- a/bsnes/sfc/chip/spc7110/decomp.hpp +++ b/bsnes/sfc/chip/spc7110/decomp.hpp @@ -1,5 +1,4 @@ -class Decomp { -public: +struct Decomp { uint8 read(); void init(unsigned mode, unsigned offset, unsigned index); void reset(); diff --git a/bsnes/sfc/chip/spc7110/rtc.cpp b/bsnes/sfc/chip/spc7110/rtc.cpp new file mode 100755 index 000000000..fefc12ff4 --- /dev/null +++ b/bsnes/sfc/chip/spc7110/rtc.cpp @@ -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 diff --git a/bsnes/sfc/chip/spc7110/serialization.cpp b/bsnes/sfc/chip/spc7110/serialization.cpp index 2056df256..6b07287a6 100755 --- a/bsnes/sfc/chip/spc7110/serialization.cpp +++ b/bsnes/sfc/chip/spc7110/serialization.cpp @@ -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 diff --git a/bsnes/sfc/chip/spc7110/spc7110.cpp b/bsnes/sfc/chip/spc7110/spc7110.cpp index f58b948e4..ce6bc4562 100755 --- a/bsnes/sfc/chip/spc7110/spc7110.cpp +++ b/bsnes/sfc/chip/spc7110/spc7110.cpp @@ -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::max() - rtc_time + current_time + 1); //compensate for overflow - if(diff > std::numeric_limits::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) { } diff --git a/bsnes/sfc/chip/spc7110/spc7110.hpp b/bsnes/sfc/chip/spc7110/spc7110.hpp index ac0d2009d..278d0eb18 100755 --- a/bsnes/sfc/chip/spc7110/spc7110.hpp +++ b/bsnes/sfc/chip/spc7110/spc7110.hpp @@ -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; diff --git a/bsnes/sfc/controller/mouse/mouse.cpp b/bsnes/sfc/controller/mouse/mouse.cpp index 92beaa0ea..9bc6f53df 100755 --- a/bsnes/sfc/controller/mouse/mouse.cpp +++ b/bsnes/sfc/controller/mouse/mouse.cpp @@ -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 diff --git a/bsnes/sfc/controller/mouse/mouse.hpp b/bsnes/sfc/controller/mouse/mouse.hpp index 95e24b65a..cc5871b04 100755 --- a/bsnes/sfc/controller/mouse/mouse.hpp +++ b/bsnes/sfc/controller/mouse/mouse.hpp @@ -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; }; diff --git a/bsnes/sfc/interface/interface.cpp b/bsnes/sfc/interface/interface.cpp index ced709013..f9944c1ef 100755 --- a/bsnes/sfc/interface/interface.cpp +++ b/bsnes/sfc/interface/interface.cpp @@ -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) { diff --git a/bsnes/sfc/system/system.cpp b/bsnes/sfc/system/system.cpp index 05cf1a462..71cee1e84 100755 --- a/bsnes/sfc/system/system.cpp +++ b/bsnes/sfc/system/system.cpp @@ -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);