#include "../copyright" #include "spc7110.h" #include "spc7110dec.h" #include "memmap.h" #include const char* S9xGetFilename(const char*); SPC7110Regs s7r; // SPC7110 registers, about 33KB S7RTC rtc_f9; // FEOEZ (and Shounen Jump no SHou) RTC void S9xUpdateRTC(void); // S-RTC function hacked to work with the RTC void S9xSpc7110Init(void) // Emulate power on state { spc7110dec_init(); s7r.DataRomOffset = 0x00100000; //handy constant! s7r.DataRomSize = Memory.CalculatedSize - s7r.DataRomOffset; s7r.reg4800 = 0; s7r.reg4801 = 0; s7r.reg4802 = 0; s7r.reg4803 = 0; s7r.reg4804 = 0; s7r.reg4805 = 0; s7r.reg4806 = 0; s7r.reg4807 = 0; s7r.reg4808 = 0; s7r.reg4809 = 0; s7r.reg480A = 0; s7r.reg480B = 0; s7r.reg480C = 0; s7r.reg4811 = 0; s7r.reg4812 = 0; s7r.reg4813 = 0; s7r.reg4814 = 0; s7r.reg4815 = 0; s7r.reg4816 = 0; s7r.reg4817 = 0; s7r.reg4818 = 0; s7r.reg4820 = 0; s7r.reg4821 = 0; s7r.reg4822 = 0; s7r.reg4823 = 0; s7r.reg4824 = 0; s7r.reg4825 = 0; s7r.reg4826 = 0; s7r.reg4827 = 0; s7r.reg4828 = 0; s7r.reg4829 = 0; s7r.reg482A = 0; s7r.reg482B = 0; s7r.reg482C = 0; s7r.reg482D = 0; s7r.reg482E = 0; s7r.reg482F = 0; s7r.reg4830 = 0; s7r.reg4831 = 0; s7r.reg4832 = 1; s7r.reg4833 = 2; s7r.reg4834 = 0; s7r.reg4840 = 0; s7r.reg4841 = 0; s7r.reg4842 = 0; s7r.written = 0; s7r.offset_add = 0; s7r.AlignBy = 1; s7r.bank50Internal = 0; memset(s7r.bank50, 0x00, DECOMP_BUFFER_SIZE); } //reads SPC7110 and RTC registers. uint8_t S9xGetSPC7110(uint16_t Address) { switch (Address) { //decompressed data read port. decrements 4809-A (with wrap) //4805-6 is the offset into the bank //AlignBy is set (afaik) at decompression time, and is the offset multiplier //bank50internal is an internal pointer to the actual byte to read. //so you read from offset*multiplier + bank50internal //the offset registers cannot be incremented due to the offset multiplier. case 0x4800: { uint16_t count = s7r.reg4809 | (s7r.reg480A << 8); if (count > 0) count--; else count = 0xFFFF; s7r.reg4809 = 0x00ff & count; s7r.reg480A = (0xff00 & count) >> 8; s7r.reg4800 = spc7110dec_read(); return s7r.reg4800; } case 0x4801: //table register low return s7r.reg4801; case 0x4802: //table register middle return s7r.reg4802; case 0x4803: //table register high return s7r.reg4803; case 0x4804: //index of pointer in table (each entry is 4 bytes) return s7r.reg4804; case 0x4805: //offset register low return s7r.reg4805; case 0x4806: //offset register high return s7r.reg4806; //DMA channel (not that I see this usually set, //regardless of what channel DMA is on) case 0x4807: return s7r.reg4807; //C r/w option, unknown, defval:00 is what Dark Force says //afaict, Snes9x doesn't use this at all. case 0x4808: return s7r.reg4808; //C-Length low //counts down the number of bytes left to read from the decompression buffer. //this is set by the ROM, and wraps on bounds. case 0x4809: return s7r.reg4809; case 0x480A: //C Length high return s7r.reg480A; //Offset enable. //if this is zero, 4805-6 are useless. Emulated by setting AlignBy to 0 case 0x480B: return s7r.reg480B; case 0x480C: //decompression finished: just emulated by switching each read. s7r.reg480C ^= 0x80; return s7r.reg480C ^ 0x80; //Data access port //reads from the data ROM (anywhere over the first 8 mbits //behavior is complex, will document later, //possibly missing cases, because of the number of switches in play case 0x4810: if (s7r.written == 0) return 0; if ((s7r.written & 0x07) == 0x07) { uint32_t i = (s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811; i %= s7r.DataRomSize; if (s7r.reg4818 & 0x02) { if (s7r.reg4818 & 0x08) { int16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; i += r4814; r4814++; s7r.reg4815 = (uint8_t)(r4814 >> 8); s7r.reg4814 = (uint8_t)(r4814 & 0x00FF); } else { uint16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; i += r4814; if (r4814 != 0xFFFF) r4814++; else r4814 = 0; s7r.reg4815 = (uint8_t)(r4814 >> 8); s7r.reg4814 = (uint8_t)(r4814 & 0x00FF); } } i += s7r.DataRomOffset; uint8_t tmp = Memory.ROM[i]; i = (s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811; if (s7r.reg4818 & 0x02) { } else if (s7r.reg4818 & 0x01) { if (s7r.reg4818 & 0x04) { int16_t inc = (s7r.reg4817 << 8) | s7r.reg4816; if (!(s7r.reg4818 & 0x10)) i += inc; else { if (s7r.reg4818 & 0x08) { int16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; r4814 += inc; s7r.reg4815 = (r4814 & 0xFF00) >> 8; s7r.reg4814 = r4814 & 0xFF; } else { uint16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; r4814 += inc; s7r.reg4815 = (r4814 & 0xFF00) >> 8; s7r.reg4814 = r4814 & 0xFF; } } //is signed } else { uint16_t inc; inc = (s7r.reg4817 << 8) | s7r.reg4816; if (!(s7r.reg4818 & 0x10)) i += inc; else { if (s7r.reg4818 & 0x08) { int16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; r4814 += inc; s7r.reg4815 = (r4814 & 0xFF00) >> 8; s7r.reg4814 = r4814 & 0xFF; } else { uint16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; r4814 += inc; s7r.reg4815 = (r4814 & 0xFF00) >> 8; s7r.reg4814 = r4814 & 0xFF; } } } } else { if (!(s7r.reg4818 & 0x10)) i += 1; else { if (s7r.reg4818 & 0x08) { int16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; r4814 += 1; s7r.reg4815 = (r4814 & 0xFF00) >> 8; s7r.reg4814 = r4814 & 0xFF; } else { uint16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; r4814 += 1; s7r.reg4815 = (r4814 & 0xFF00) >> 8; s7r.reg4814 = r4814 & 0xFF; } } } i %= s7r.DataRomSize; s7r.reg4811 = i & 0x00FF; s7r.reg4812 = (i & 0x00FF00) >> 8; s7r.reg4813 = ((i & 0xFF0000) >> 16); return tmp; } else return 0; case 0x4811: //direct read address low return s7r.reg4811; case 0x4812: //direct read address middle return s7r.reg4812; case 0x4813: //direct read access high return s7r.reg4813; case 0x4814: //read adjust low return s7r.reg4814; case 0x4815: //read adjust high return s7r.reg4815; case 0x4816: //read increment low return s7r.reg4816; case 0x4817: //read increment high return s7r.reg4817; case 0x4818: //Data ROM command mode; essentially, this controls the insane code of $4810 and $481A return s7r.reg4818; //read after adjust port //what this does, besides more nasty stuff like 4810, //I don't know. Just assume it is a different implementation of $4810, //if it helps your sanity case 0x481A: if (s7r.written == 0x1F) { uint32_t i = ((s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811); if (s7r.reg4818 & 0x08) i += ((int16_t)(s7r.reg4815 << 8)) | s7r.reg4814; else i += (s7r.reg4815 << 8) | s7r.reg4814; i %= s7r.DataRomSize; i += s7r.DataRomOffset; uint8_t tmp = Memory.ROM[i]; if ((s7r.reg4818 & 0x60) == 0x60) { i = ((s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811); if (!(s7r.reg4818 & 0x10)) { if (s7r.reg4818 & 0x08) { int16_t adj = ((int16_t)(s7r.reg4815 << 8)) | s7r.reg4814; i += adj; } else { uint16_t adj; adj = (s7r.reg4815 << 8) | s7r.reg4814; i += adj; } i %= s7r.DataRomSize; s7r.reg4811 = i & 0x00FF; s7r.reg4812 = (i & 0x00FF00) >> 8; s7r.reg4813 = ((i & 0xFF0000) >> 16); } else { if (s7r.reg4818 & 0x08) { int16_t adj = ((int16_t)(s7r.reg4815 << 8)) | s7r.reg4814; adj += adj; s7r.reg4815 = (adj & 0xFF00) >> 8; s7r.reg4814 = adj & 0xFF; } else { uint16_t adj; adj = (s7r.reg4815 << 8) | s7r.reg4814; adj += adj; s7r.reg4815 = (adj & 0xFF00) >> 8; s7r.reg4814 = adj & 0xFF; } } } return tmp; } else return 0; case 0x4820: //multiplicand low or dividend lowest return s7r.reg4820; case 0x4821: //multiplicand high or divdend lower return s7r.reg4821; case 0x4822: //dividend higher return s7r.reg4822; case 0x4823: //dividend highest return s7r.reg4823; case 0x4824: //multiplier low return s7r.reg4824; case 0x4825: //multiplier high return s7r.reg4825; case 0x4826: //divisor low return s7r.reg4826; case 0x4827: //divisor high return s7r.reg4827; case 0x4828: //result lowest return s7r.reg4828; case 0x4829: //result lower return s7r.reg4829; case 0x482A: //result higher return s7r.reg482A; case 0x482B: //result highest return s7r.reg482B; case 0x482C: //remainder (division) low return s7r.reg482C; case 0x482D: //remainder (division) high return s7r.reg482D; case 0x482E: //signed/unsigned return s7r.reg482E; case 0x482F: //finished flag, emulated as an on-read toggle. if (s7r.reg482F) { s7r.reg482F = 0; return 0x80; } return 0; case 0x4830: //SRAM toggle return s7r.reg4830; case 0x4831: //DX bank mapping return s7r.reg4831; case 0x4832: //EX bank mapping return s7r.reg4832; case 0x4833: //FX bank mapping return s7r.reg4833; case 0x4834: //SRAM mapping? We have no clue! return s7r.reg4834; case 0x4840: //RTC enable if (!Settings.SPC7110RTC) return Address >> 8; return s7r.reg4840; case 0x4841: //command/index/value of RTC (essentially, zero unless we're in read mode if (!Settings.SPC7110RTC) return Address >> 8; if (rtc_f9.init) { S9xUpdateRTC(); uint8_t tmp = rtc_f9.reg[rtc_f9.index]; rtc_f9.index++; rtc_f9.index %= 0x10; return tmp; } else return 0; case 0x4842: //RTC done flag if (!Settings.SPC7110RTC) return Address >> 8; s7r.reg4842 ^= 0x80; return s7r.reg4842 ^ 0x80; default: return 0x00; } } static uint32_t datarom_addr(uint32_t addr) { uint32_t size = Memory.CalculatedSize - 0x100000; while(addr >= size) addr -= size; return addr + 0x100000; } void S9xSetSPC7110(uint8_t data, uint16_t Address) { switch (Address) { //Writes to $4800 are undefined. case 0x4801: //table low, middle, and high. s7r.reg4801 = data; break; case 0x4802: s7r.reg4802 = data; break; case 0x4803: s7r.reg4803 = data; break; case 0x4804: //table index (4 byte entries, bigendian with a multiplier byte) s7r.reg4804 = data; break; case 0x4805: //offset low s7r.reg4805 = data; break; case 0x4806: //offset high, starts decompression { uint32_t table = (s7r.reg4801 + (s7r.reg4802 << 8) + (s7r.reg4803 << 16)); uint32_t index = (s7r.reg4804 << 2); uint32_t addr = datarom_addr(table + index); uint32_t mode = (Memory.ROM[addr + 0]); uint32_t offset = (Memory.ROM[addr + 1] << 16) + (Memory.ROM[addr + 2] << 8) + (Memory.ROM[addr + 3]); s7r.reg4806 = data; spc7110dec_clear(mode, offset, (s7r.reg4805 + (s7r.reg4806 << 8)) << mode); s7r.bank50Internal = 0; s7r.reg480C &= 0x7F; break; } case 0x4807: //DMA channel register (Is it used??) s7r.reg4807 = data; break; //C r/w? I have no idea. If you get weird values written here before a bug, //The Dumper should probably be contacted about running a test. case 0x4808: s7r.reg4808 = data; break; case 0x4809: //C-Length low s7r.reg4809 = data; break; case 0x480A: //C-Length high s7r.reg480A = data; break; case 0x480B: //Offset enable { s7r.reg480B = data; int32_t table = (s7r.reg4803 << 16) | (s7r.reg4802 << 8) | s7r.reg4801; int32_t j = 4 * s7r.reg4804 + s7r.DataRomOffset + table; if (s7r.reg480B == 0) s7r.AlignBy = 0; else { switch (Memory.ROM[j]) { case 0x03: s7r.AlignBy = 8; break; case 0x01: s7r.AlignBy = 2; break; case 0x02: s7r.AlignBy = 4; break; case 0x00: default: s7r.AlignBy = 1; break; } } break; } case 0x4811: //Data port address low s7r.reg4811 = data; s7r.written |= 0x01; break; case 0x4812: //data port address middle s7r.reg4812 = data; s7r.written |= 0x02; break; case 0x4813: //data port address high s7r.reg4813 = data; s7r.written |= 0x04; break; case 0x4814: //data port adjust low (has a funky immediate increment mode) s7r.reg4814 = data; if (s7r.reg4818 & 0x02) { if ((s7r.reg4818 & 0x20) && !(s7r.reg4818 & 0x40)) { s7r.offset_add |= 0x01; if (s7r.offset_add == 3) { if(!(s7r.reg4818 & 0x10)) { uint32_t i = (s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811; if (s7r.reg4818 & 0x08) i += (int8_t)s7r.reg4814; else i += s7r.reg4814; i %= s7r.DataRomSize; s7r.reg4811 = i & 0x00FF; s7r.reg4812 = (i & 0x00FF00) >> 8; s7r.reg4813 = ((i & 0xFF0000) >> 16); } } } else if ((s7r.reg4818 & 0x40) && !(s7r.reg4818 & 0x20)) { s7r.offset_add |= 0x01; if (s7r.offset_add == 3) { if(!(s7r.reg4818 & 0x10)) { uint32_t i = (s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811; if (s7r.reg4818 & 0x08) i += ((int16_t)(s7r.reg4815 << 8)) | s7r.reg4814; else i += (s7r.reg4815 << 8) | s7r.reg4814; i %= s7r.DataRomSize; s7r.reg4811 = i & 0x00FF; s7r.reg4812 = (i & 0x00FF00) >> 8; s7r.reg4813 = ((i & 0xFF0000) >> 16); } } } } s7r.written |= 0x08; break; case 0x4815: //data port adjust high (has a funky immediate increment mode) s7r.reg4815 = data; if (s7r.reg4818 & 0x02) { if (s7r.reg4818 & 0x20 && !(s7r.reg4818 & 0x40)) { s7r.offset_add |= 0x02; if (s7r.offset_add == 3) { if(!(s7r.reg4818 & 0x10)) { uint32_t i = (s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811; if (s7r.reg4818 & 0x08) i += (int8_t)s7r.reg4814; else i += s7r.reg4814; i %= s7r.DataRomSize; s7r.reg4811 = i & 0x00FF; s7r.reg4812 = (i & 0x00FF00) >> 8; s7r.reg4813 = ((i & 0xFF0000) >> 16); } } } else if (s7r.reg4818 & 0x40 && !(s7r.reg4818 & 0x20)) { s7r.offset_add |= 0x02; if (s7r.offset_add == 3) { if(!(s7r.reg4818 & 0x10)) { uint32_t i = (s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811; if (s7r.reg4818 & 0x08) i += ((int16_t)(s7r.reg4815 << 8)) | s7r.reg4814; else i += (s7r.reg4815 << 8) | s7r.reg4814; i %= s7r.DataRomSize; s7r.reg4811 = i & 0x00FF; s7r.reg4812 = (i & 0x00FF00) >> 8; s7r.reg4813 = ((i & 0xFF0000) >> 16); } } } } s7r.written |= 0x10; break; case 0x4816: //data port increment low s7r.reg4816 = data; break; case 0x4817: //data port increment high s7r.reg4817 = data; break; //data port mode switches //note that it starts inactive. case 0x4818: if ((s7r.written & 0x18) != 0x18) break; s7r.offset_add = 0; s7r.reg4818 = data; break; case 0x4820: //multiplicand low or dividend lowest s7r.reg4820 = data; break; case 0x4821: //multiplicand high or dividend lower s7r.reg4821 = data; break; case 0x4822: //dividend higher s7r.reg4822 = data; break; case 0x4823: //dividend highest s7r.reg4823 = data; break; case 0x4824: //multiplier low s7r.reg4824 = data; break; case 0x4825: //multiplier high (triggers operation) s7r.reg4825 = data; if (s7r.reg482E & 0x01) { int16_t m1 = (int16_t)((s7r.reg4824) | (s7r.reg4825 << 8)); int16_t m2 = (int16_t)((s7r.reg4820) | (s7r.reg4821 << 8)); int32_t mul = m1 * m2; s7r.reg4828 = (uint8_t)(mul & 0x000000FF); s7r.reg4829 = (uint8_t)((mul & 0x0000FF00) >> 8); s7r.reg482A = (uint8_t)((mul & 0x00FF0000) >> 16); s7r.reg482B = (uint8_t)((mul & 0xFF000000) >> 24); } else { uint16_t m1 = (uint16_t)((s7r.reg4824) | (s7r.reg4825 << 8)); uint16_t m2 = (uint16_t)((s7r.reg4820) | (s7r.reg4821 << 8)); uint32_t mul = m1 * m2; s7r.reg4828 = (uint8_t)(mul & 0x000000FF); s7r.reg4829 = (uint8_t)((mul & 0x0000FF00) >> 8); s7r.reg482A = (uint8_t)((mul & 0x00FF0000) >> 16); s7r.reg482B = (uint8_t)((mul & 0xFF000000) >> 24); } s7r.reg482F = 0x80; break; case 0x4826: //divisor low s7r.reg4826 = data; break; case 0x4827: //divisor high (triggers operation) s7r.reg4827 = data; if (s7r.reg482E & 0x01) { int32_t quotient; int16_t remainder; int32_t dividend = (int32_t)(s7r.reg4820 | (s7r.reg4821 << 8) | (s7r.reg4822 << 16) | (s7r.reg4823 << 24)); int16_t divisor = (int16_t)(s7r.reg4826 | (s7r.reg4827 << 8)); if (divisor != 0) { quotient = (int32_t)(dividend / divisor); remainder = (int16_t)(dividend % divisor); } else { quotient = 0; remainder = dividend & 0x0000FFFF; } s7r.reg4828 = (uint8_t)(quotient & 0x000000FF); s7r.reg4829 = (uint8_t)((quotient & 0x0000FF00) >> 8); s7r.reg482A = (uint8_t)((quotient & 0x00FF0000) >> 16); s7r.reg482B = (uint8_t)((quotient & 0xFF000000) >> 24); s7r.reg482C = (uint8_t)remainder & 0x00FF; s7r.reg482D = (uint8_t)((remainder & 0xFF00) >> 8); } else { uint32_t quotient; uint16_t remainder; uint32_t dividend = (uint32_t)(s7r.reg4820 | (s7r.reg4821 << 8) | (s7r.reg4822 << 16) | (s7r.reg4823 << 24)); uint16_t divisor = (uint16_t)(s7r.reg4826 | (s7r.reg4827 << 8)); if (divisor != 0) { quotient = (uint32_t)(dividend / divisor); remainder = (uint16_t)(dividend % divisor); } else { quotient = 0; remainder = dividend & 0x0000FFFF; } s7r.reg4828 = (uint8_t)(quotient & 0x000000FF); s7r.reg4829 = (uint8_t)((quotient & 0x0000FF00) >> 8); s7r.reg482A = (uint8_t)((quotient & 0x00FF0000) >> 16); s7r.reg482B = (uint8_t)((quotient & 0xFF000000) >> 24); s7r.reg482C = (uint8_t)remainder & 0x00FF; s7r.reg482D = (uint8_t)((remainder & 0xFF00) >> 8); } s7r.reg482F = 0x80; break; //result registers are possibly read-only //reset: writes here nuke the whole math unit //Zero indicates unsigned math, resets with non-zero values turn on signed math case 0x482E: s7r.reg4820 = s7r.reg4821 = s7r.reg4822 = s7r.reg4823 = s7r.reg4824 = s7r.reg4825 = s7r.reg4826 = s7r.reg4827 = s7r.reg4828 = s7r.reg4829 = s7r.reg482A = s7r.reg482B = s7r.reg482C = s7r.reg482D = 0; s7r.reg482E = data; break; //math status register possibly read only case 0x4830: //SRAM toggle SPC7110Sram(data); s7r.reg4830 = data; break; case 0x4831: //Bank DX mapping s7r.reg4831 = data; break; case 0x4832: //Bank EX mapping s7r.reg4832 = data; break; case 0x4833: //Bank FX mapping s7r.reg4833 = data; break; case 0x4834: //S-RAM mapping? who knows? s7r.reg4834 = data; break; case 0x4840: //RTC Toggle if(!data) S9xUpdateRTC(); else if(data & 0x01) { s7r.reg4842 = 0x80; rtc_f9.init = false; rtc_f9.index = -1; } s7r.reg4840 = data; break; case 0x4841: //RTC init/command/index register if (rtc_f9.init) { if (rtc_f9.index == -1) { rtc_f9.index = data & 0x0F; break; } if (rtc_f9.control == 0x0C) { rtc_f9.index = data & 0x0F; s7r.reg4842 = 0x80; rtc_f9.last_used = time(NULL); } else { if (rtc_f9.index == 0x0D) { if (data & 0x08) { if (rtc_f9.reg[1] < 3) { S9xUpdateRTC(); rtc_f9.reg[0] = 0; rtc_f9.reg[1] = 0; rtc_f9.last_used = time(NULL); } else { S9xUpdateRTC(); rtc_f9.reg[0] = 0; rtc_f9.reg[1] = 0; rtc_f9.last_used = time(NULL) - 60; S9xUpdateRTC(); rtc_f9.last_used = time(NULL); } data &= 0x07; } if (rtc_f9.reg[0x0D] & 0x01) { if (!(data % 2)) { rtc_f9.reg[rtc_f9.index & 0x0F] = data; rtc_f9.last_used = time(NULL) - 1; S9xUpdateRTC(); rtc_f9.last_used = time(NULL); } } } if (rtc_f9.index == 0x0F) { if (data & 0x01 && !(rtc_f9.reg[0x0F] & 0x01)) { S9xUpdateRTC(); rtc_f9.reg[0] = 0; rtc_f9.reg[1] = 0; rtc_f9.last_used = time(NULL); } if (data & 0x02 && !(rtc_f9.reg[0x0F] & 0x02)) { S9xUpdateRTC(); rtc_f9.last_used = time(NULL); } } rtc_f9.reg[rtc_f9.index & 0x0F] = data; s7r.reg4842 = 0x80; rtc_f9.index = (rtc_f9.index + 1) % 0x10; } } else { if (data == 0x03 || data == 0x0C) { rtc_f9.init = true; rtc_f9.control = data; rtc_f9.index = -1; } } break; } } //emulate the SPC7110's ability to remap banks Dx, Ex, and Fx. uint8_t S9xGetSPC7110Byte(uint32_t Address) { uint32_t i; switch ((Address & 0x00F00000) >> 16) { case 0xD0: i = s7r.reg4831 * 0x00100000; break; case 0xE0: i = s7r.reg4832 * 0x00100000; break; case 0xF0: i = s7r.reg4833 * 0x00100000; break; default: i = 0; } i += Address & 0x000FFFFF; i += s7r.DataRomOffset; return Memory.ROM[i]; } /**********************************************************************************************/ /* S9xSRTCDaysInMonth() */ /* Return the number of days in a specific month for a certain year */ /* copied directly for RTC functionality, separated in case of incompatibilities */ /**********************************************************************************************/ int32_t S9xRTCDaysInMonth(int32_t month, int32_t year) { switch(month) { case 2: if(year % 4 == 0) // DKJM2 only uses 199x - 22xx return 29; return 28; case 4: case 6: case 9: case 11: return 30; default: // months 1,3,5,7,8,10,12 return 31; } } #define MINUTETICKS 60 #define HOURTICKS (60 * MINUTETICKS) #define DAYTICKS (24 * HOURTICKS) /**********************************************************************************************/ /* S9xUpdateRTC() */ /* Advance the RTC time */ /**********************************************************************************************/ void S9xUpdateRTC(void) { time_t cur_systime; int32_t time_diff; // Keep track of game time by computing the number of seconds that pass on the system // clock and adding the same number of seconds to the RTC clock structure. if (rtc_f9.init && 0 == (rtc_f9.reg[0x0D] & 0x01) && 0 == (rtc_f9.reg[0x0F] & 0x03)) { cur_systime = time(NULL); // This method assumes one time_t clock tick is one second // which should work on PCs and GNU systems. // If your tick interval is different adjust the // DAYTICK, HOURTICK, and MINUTETICK defines time_diff = (int32_t)(cur_systime - rtc_f9.last_used); rtc_f9.last_used = cur_systime; if (time_diff > 0) { int32_t seconds; int32_t minutes; int32_t hours; int32_t days; int32_t month; int32_t year; int32_t temp_days; int32_t year_tens; int32_t year_ones; if (time_diff > DAYTICKS) { days = time_diff / DAYTICKS; time_diff = time_diff - days * DAYTICKS; } else days = 0; if (time_diff > HOURTICKS) { hours = time_diff / HOURTICKS; time_diff = time_diff - hours * HOURTICKS; } else hours = 0; if (time_diff > MINUTETICKS) { minutes = time_diff / MINUTETICKS; time_diff = time_diff - minutes * MINUTETICKS; } else minutes = 0; if (time_diff > 0) seconds = time_diff; else seconds = 0; seconds += (rtc_f9.reg[1] * 10 + rtc_f9.reg[0]); if (seconds >= 60) { seconds -= 60; minutes += 1; } minutes += (rtc_f9.reg[3] * 10 + rtc_f9.reg[2]); if (minutes >= 60) { minutes -= 60; hours += 1; } hours += (rtc_f9.reg[5] * 10 + rtc_f9.reg[4]); if (hours >= 24) { hours -= 24; days += 1; } year = rtc_f9.reg[11] * 10 + rtc_f9.reg[10]; year += (1900); month = rtc_f9.reg[8] + 10 * rtc_f9.reg[9]; rtc_f9.reg[12] += days; days += (rtc_f9.reg[7] * 10 + rtc_f9.reg[6]); if (days > 0) { while (days > (temp_days = S9xRTCDaysInMonth(month, year))) { days -= temp_days; month += 1; if (month > 12) { year += 1; month = 1; } } } year_tens = year % 100; year_ones = year_tens % 10; year_tens /= 10; rtc_f9.reg[0] = seconds % 10; rtc_f9.reg[1] = seconds / 10; rtc_f9.reg[2] = minutes % 10; rtc_f9.reg[3] = minutes / 10; rtc_f9.reg[4] = hours % 10; rtc_f9.reg[5] = hours / 10; rtc_f9.reg[6] = days % 10; rtc_f9.reg[7] = days / 10; rtc_f9.reg[8] = month % 10; rtc_f9.reg[9] = month / 10; rtc_f9.reg[10] = year_ones; rtc_f9.reg[11] = year_tens; rtc_f9.reg[12] %= 7; return; } } } //allows DMA from the ROM (is this even possible on the SPC7110? uint8_t* Get7110BasePtr(uint32_t Address) { uint32_t i; switch ((Address & 0x00F00000) >> 16) { case 0xD0: i = s7r.reg4831 * 0x100000; break; case 0xE0: i = s7r.reg4832 * 0x100000; break; case 0xF0: i = s7r.reg4833 * 0x100000; break; default: i = 0; } i += Address & 0x000F0000; return &Memory.ROM[i]; } //Cache 1 clean up function void Del7110Gfx(void) { spc7110dec_deinit(); Settings.SPC7110 = false; Settings.SPC7110RTC = false; } //emulate a reset. void S9xSpc7110Reset(void) { s7r.reg4800 = 0; s7r.reg4801 = 0; s7r.reg4802 = 0; s7r.reg4803 = 0; s7r.reg4804 = 0; s7r.reg4805 = 0; s7r.reg4806 = 0; s7r.reg4807 = 0; s7r.reg4808 = 0; s7r.reg4809 = 0; s7r.reg480A = 0; s7r.reg480B = 0; s7r.reg480C = 0; s7r.reg4811 = 0; s7r.reg4812 = 0; s7r.reg4813 = 0; s7r.reg4814 = 0; s7r.reg4815 = 0; s7r.reg4816 = 0; s7r.reg4817 = 0; s7r.reg4818 = 0; s7r.reg4820 = 0; s7r.reg4821 = 0; s7r.reg4822 = 0; s7r.reg4823 = 0; s7r.reg4824 = 0; s7r.reg4825 = 0; s7r.reg4826 = 0; s7r.reg4827 = 0; s7r.reg4828 = 0; s7r.reg4829 = 0; s7r.reg482A = 0; s7r.reg482B = 0; s7r.reg482C = 0; s7r.reg482D = 0; s7r.reg482E = 0; s7r.reg482F = 0; s7r.reg4830 = 0; s7r.reg4831 = 0; s7r.reg4832 = 1; s7r.reg4833 = 2; s7r.reg4834 = 0; s7r.reg4840 = 0; s7r.reg4841 = 0; s7r.reg4842 = 0; s7r.written = 0; s7r.offset_add = 0; s7r.AlignBy = 1; s7r.bank50Internal = 0; memset(s7r.bank50, 0x00, DECOMP_BUFFER_SIZE); spc7110dec_reset(); }