snes9x2005/source/spc7110.c

1091 lines
32 KiB
C

#include "../copyright"
#include "spc7110.h"
#include "spc7110dec.h"
#include "memmap.h"
#include <time.h>
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();
}