123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672 |
- #include <../base.hpp>
- #include <../cart/cart.hpp>
- #define SPC7110_CPP
- #include "spc7110.hpp"
- #include "decomp.cpp"
- const unsigned SPC7110::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
- void SPC7110::init() {}
- void SPC7110::enable() {
- uint16_t limit = (cartridge.has_spc7110rtc() ? 0x4842 : 0x483f);
- for(uint16_t i = 0x4800; i <= limit; i++) memory::mmio.map(i, *this);
- }
- void SPC7110::power() {
- reset();
- }
- void SPC7110::reset() {
- r4801 = 0x00;
- r4802 = 0x00;
- r4803 = 0x00;
- r4804 = 0x00;
- r4805 = 0x00;
- r4806 = 0x00;
- r4807 = 0x00;
- r4808 = 0x00;
- r4809 = 0x00;
- r480a = 0x00;
- r480b = 0x00;
- r480c = 0x00;
- decomp.reset();
- r4811 = 0x00;
- r4812 = 0x00;
- r4813 = 0x00;
- r4814 = 0x00;
- r4815 = 0x00;
- r4816 = 0x00;
- r4817 = 0x00;
- r4818 = 0x00;
- r481x = 0x00;
- r4814_latch = false;
- r4815_latch = false;
- r4820 = 0x00;
- r4821 = 0x00;
- r4822 = 0x00;
- r4823 = 0x00;
- r4824 = 0x00;
- r4825 = 0x00;
- r4826 = 0x00;
- r4827 = 0x00;
- r4828 = 0x00;
- r4829 = 0x00;
- r482a = 0x00;
- r482b = 0x00;
- r482c = 0x00;
- r482d = 0x00;
- r482e = 0x00;
- r482f = 0x00;
- r4830 = 0x00;
- mmio_write(0x4831, 0);
- mmio_write(0x4832, 1);
- mmio_write(0x4833, 2);
- r4834 = 0x00;
- r4840 = 0x00;
- 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) {
- unsigned size = memory::cartrom.size() - 0x100000;
- while(addr >= size) addr -= size;
- return addr + 0x100000;
- }
- 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
- = (memory::cartrtc.read(16) << 0)
- | (memory::cartrtc.read(17) << 8)
- | (memory::cartrtc.read(18) << 16)
- | (memory::cartrtc.read(19) << 24);
- time_t current_time = time(0) - offset;
- //sizeof(time_t) is platform-dependent; though memory::cartrtc 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
- //memory::cartrtc 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(memory::cartrtc.read(13) & 1) update = false; //do not update if CR0 timer disable flag is set
- if(memory::cartrtc.read(15) & 3) update = false; //do not update if CR2 timer disable flags are set
- if(diff > 0 && update == true) {
- unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10;
- unsigned minute = memory::cartrtc.read( 2) + memory::cartrtc.read( 3) * 10;
- unsigned hour = memory::cartrtc.read( 4) + memory::cartrtc.read( 5) * 10;
- unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10;
- unsigned month = memory::cartrtc.read( 8) + memory::cartrtc.read( 9) * 10;
- unsigned year = memory::cartrtc.read(10) + memory::cartrtc.read(11) * 10;
- unsigned weekday = memory::cartrtc.read(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;
- memory::cartrtc.write( 0, second % 10);
- memory::cartrtc.write( 1, second / 10);
- memory::cartrtc.write( 2, minute % 10);
- memory::cartrtc.write( 3, minute / 10);
- memory::cartrtc.write( 4, hour % 10);
- memory::cartrtc.write( 5, hour / 10);
- memory::cartrtc.write( 6, day % 10);
- memory::cartrtc.write( 7, day / 10);
- memory::cartrtc.write( 8, month % 10);
- memory::cartrtc.write( 9, month / 10);
- memory::cartrtc.write(10, year % 10);
- memory::cartrtc.write(11, (year / 10) % 10);
- memory::cartrtc.write(12, weekday % 7);
- }
- memory::cartrtc.write(16, current_time >> 0);
- memory::cartrtc.write(17, current_time >> 8);
- memory::cartrtc.write(18, current_time >> 16);
- memory::cartrtc.write(19, current_time >> 24);
- }
- uint8 SPC7110::mmio_read(unsigned addr) {
- addr &= 0xffff;
- switch(addr) {
- //==================
- //decompression unit
- //==================
- case 0x4800: {
- uint16 counter = (r4809 + (r480a << 8));
- counter--;
- r4809 = counter;
- r480a = counter >> 8;
- return decomp.read();
- }
- case 0x4801: return r4801;
- case 0x4802: return r4802;
- case 0x4803: return r4803;
- case 0x4804: return r4804;
- case 0x4805: return r4805;
- case 0x4806: return r4806;
- case 0x4807: return r4807;
- case 0x4808: return r4808;
- case 0x4809: return r4809;
- case 0x480a: return r480a;
- case 0x480b: return r480b;
- case 0x480c: {
- uint8 status = r480c;
- r480c &= 0x7f;
- return status;
- }
- //==============
- //data port unit
- //==============
- 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 = memory::cartrom.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);
- }
- }
- return data;
- }
- case 0x4811: return r4811;
- case 0x4812: return r4812;
- case 0x4813: return r4813;
- case 0x4814: return r4814;
- case 0x4815: return r4815;
- case 0x4816: return r4816;
- 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 = memory::cartrom.read(datarom_addr(addr + adjust));
- if((r4818 & 0x60) == 0x60) {
- if((r4818 & 16) == 0) {
- set_data_pointer(addr + adjust);
- } else {
- set_data_adjust(adjust + adjust);
- }
- }
- return data;
- }
- //=========
- //math unit
- //=========
- case 0x4820: return r4820;
- case 0x4821: return r4821;
- case 0x4822: return r4822;
- case 0x4823: return r4823;
- case 0x4824: return r4824;
- case 0x4825: return r4825;
- case 0x4826: return r4826;
- case 0x4827: return r4827;
- case 0x4828: return r4828;
- case 0x4829: return r4829;
- case 0x482a: return r482a;
- case 0x482b: return r482b;
- case 0x482c: return r482c;
- case 0x482d: return r482d;
- case 0x482e: return r482e;
- case 0x482f: {
- uint8 status = r482f;
- r482f &= 0x7f;
- return status;
- }
- //===================
- //memory mapping unit
- //===================
- case 0x4830: return r4830;
- case 0x4831: return r4831;
- case 0x4832: return r4832;
- case 0x4833: return r4833;
- case 0x4834: return r4834;
- //====================
- //real-time clock unit
- //====================
- case 0x4840: return r4840;
- case 0x4841: {
- if(rtc_state == RTCS_Inactive || rtc_state == RTCS_ModeSelect) return 0x00;
- r4842 = 0x80;
- uint8 data = memory::cartrtc.read(rtc_index);
- rtc_index = (rtc_index + 1) & 15;
- return data;
- }
- case 0x4842: {
- uint8 status = r4842;
- r4842 &= 0x7f;
- return status;
- }
- }
- return cpu.regs.mdr;
- }
- void SPC7110::mmio_write(unsigned addr, uint8 data) {
- addr &= 0xffff;
- switch(addr) {
- //==================
- //decompression unit
- //==================
- case 0x4801: r4801 = data; break;
- case 0x4802: r4802 = data; break;
- case 0x4803: r4803 = data; break;
- case 0x4804: r4804 = data; break;
- case 0x4805: r4805 = data; break;
- case 0x4806: {
- r4806 = data;
- unsigned table = (r4801 + (r4802 << 8) + (r4803 << 16));
- unsigned index = (r4804 << 2);
- unsigned length = (r4809 + (r480a << 8));
- unsigned addr = datarom_addr(table + index);
- unsigned mode = (memory::cartrom.read(addr + 0));
- unsigned offset = (memory::cartrom.read(addr + 1) << 16)
- + (memory::cartrom.read(addr + 2) << 8)
- + (memory::cartrom.read(addr + 3) << 0);
- decomp.init(mode, offset, (r4805 + (r4806 << 8)) << mode);
- r480c = 0x80;
- } break;
- case 0x4807: r4807 = data; break;
- case 0x4808: r4808 = data; break;
- case 0x4809: r4809 = data; break;
- case 0x480a: r480a = data; break;
- case 0x480b: r480b = data; 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 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);
- }
- } break;
- case 0x4816: r4816 = data; break;
- case 0x4817: r4817 = data; break;
- case 0x4818: {
- if(r481x != 0x07) break;
- r4818 = data;
- r4814_latch = r4815_latch = false;
- } break;
- //=========
- //math unit
- //=========
- case 0x4820: r4820 = data; break;
- case 0x4821: r4821 = data; break;
- 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 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;
- //===================
- //memory mapping unit
- //===================
- case 0x4830: r4830 = data; break;
- case 0x4831: {
- r4831 = data;
- dx_offset = datarom_addr((data & 7) * 0x100000);
- } break;
- case 0x4832: {
- r4832 = data;
- ex_offset = datarom_addr((data & 7) * 0x100000);
- } break;
- case 0x4833: {
- r4833 = data;
- fx_offset = datarom_addr((data & 7) * 0x100000);
- } break;
- case 0x4834: r4834 = data; break;
- //====================
- //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;
- }
- } break;
- case 0x4841: {
- r4841 = data;
- 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;
- case RTCS_IndexSelect: {
- r4842 = 0x80;
- rtc_index = data & 15;
- if(rtc_mode == RTCM_Linear) rtc_state = RTCS_Write;
- } break;
- case RTCS_Write: {
- r4842 = 0x80;
- //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 = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10;
- //clear seconds
- memory::cartrtc.write(0, 0);
- memory::cartrtc.write(1, 0);
- if(second >= 30) update_time(+60);
- }
- }
- //control register 2
- if(rtc_index == 15) {
- //disable timer and clear second counter
- if((data & 1) && !(memory::cartrtc.read(15) & 1)) {
- update_time();
- //clear seconds
- memory::cartrtc.write(0, 0);
- memory::cartrtc.write(1, 0);
- }
- //disable timer
- if((data & 2) && !(memory::cartrtc.read(15) & 2)) {
- update_time();
- }
- }
- memory::cartrtc.write(rtc_index, data & 15);
- rtc_index = (rtc_index + 1) & 15;
- } break;
- } //switch(rtc_state)
- } break;
- }
- }
- uint8 SPC7110::read(unsigned addr) {
- //$[00-0f|80-8f]:[8000-ffff], $[c0-cf]:[0000-ffff] mapped directly to memory::cartrom
- if((addr & 0xffe000) == 0x006000 || (addr & 0xffe000) == 0x306000) {
- //$[00|30]:[6000-7fff]
- return memory::cartram.read(addr & 0x1fff);
- }
- if((addr & 0xff0000) == 0x500000) {
- //$[50]:[0000-ffff]
- return mmio_read(0x4800);
- }
- if((addr & 0xf00000) == 0xd00000) {
- //$[d0-df]:[0000-ffff]
- return memory::cartrom.read(dx_offset + (addr & 0x0fffff));
- }
- if((addr & 0xf00000) == 0xe00000) {
- //$[e0-ef]:[0000-ffff]
- return memory::cartrom.read(ex_offset + (addr & 0x0fffff));
- }
- if((addr & 0xf00000) == 0xf00000) {
- //$[f0-ff]:[0000-ffff]
- return memory::cartrom.read(fx_offset + (addr & 0x0fffff));
- }
- return cpu.regs.mdr;
- }
- void SPC7110::write(unsigned addr, uint8 data) {
- if((addr & 0xffe000) == 0x006000 || (addr & 0xffe000) == 0x306000) {
- //$[00|30]:[6000-7fff]
- if(r4830 & 0x80) memory::cartram.write(addr & 0x1fff, data);
- return;
- }
- }
- SPC7110::SPC7110() {
- }
|