mirror of
https://github.com/libretro/snes9x2005.git
synced 2024-11-23 16:29:43 +00:00
1091 lines
32 KiB
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();
|
|
}
|