Mesen-S/Core/Spc7110.cpp
Sour 6539b92502 SA-1: Fixed timing regressions
Memory handlers did not have the _memoryType value set, which caused some memory accesses to be much faster than expected
2020-03-01 18:24:33 -05:00

492 lines
13 KiB
C++

#include "stdafx.h"
#include "Spc7110.h"
#include "Spc7110Decomp.h"
#include "Console.h"
#include "MemoryMappings.h"
#include "BaseCartridge.h"
#include "MemoryManager.h"
#include "EmuSettings.h"
#include "BatteryManager.h"
#include "MessageManager.h"
#include "../Utilities/HexUtilities.h"
Spc7110::Spc7110(Console* console, bool useRtc) : BaseCoprocessor(SnesMemoryType::Register)
{
_console = console;
_cart = console->GetCartridge().get();
_useRtc = useRtc;
MemoryMappings* mappings = console->GetMemoryManager()->GetMemoryMappings();
vector<unique_ptr<IMemoryHandler>>& prgRomHandlers = _cart->GetPrgRomHandlers();
vector<unique_ptr<IMemoryHandler>>& saveRamHandlers = _cart->GetSaveRamHandlers();
//Regular A Bus register handler, keep a reference to it, it'll be overwritten below
_cpuRegisterHandler = mappings->GetHandler(0x4000);
//SPC7110 registers (0x4800-0x4842)
mappings->RegisterHandler(0x00, 0x3F, 0x4000, 0x4FFF, this);
mappings->RegisterHandler(0x80, 0xBF, 0x4000, 0x4FFF, this);
mappings->RegisterHandler(0x50, 0x50, 0x0000, 0xFFFF, this);
mappings->RegisterHandler(0x58, 0x58, 0x0000, 0xFFFF, this);
//SRAM
mappings->RegisterHandler(0x00, 0x3F, 0x6000, 0x7FFF, saveRamHandlers);
mappings->RegisterHandler(0x80, 0xBF, 0x6000, 0x7FFF, saveRamHandlers);
//PRG
mappings->RegisterHandler(0x00, 0x3F, 0x8000, 0xFFFF, prgRomHandlers, 8);
mappings->RegisterHandler(0x80, 0xBF, 0x8000, 0xFFFF, prgRomHandlers, 8);
bool enableStrictBoardMappings = console->GetSettings()->GetEmulationConfig().EnableStrictBoardMappings;
uint32_t romSize = _cart->DebugGetPrgRomSize();
if(!enableStrictBoardMappings && _cart->DebugGetPrgRomSize() >= 0x600000) {
mappings->RegisterHandler(0x40, 0x4F, 0x0000, 0xFFFF, prgRomHandlers, 0, 0x600);
_realDataRomSize = romSize - 0x200000;
} else {
_realDataRomSize = romSize - 0x100000;
}
mappings->RegisterHandler(0xC0, 0xCF, 0x0000, 0xFFFF, prgRomHandlers);
Reset();
}
void Spc7110::Serialize(Serializer& s)
{
ArrayInfo<uint8_t> decompBuffer = { _decompBuffer, 32 };
ArrayInfo<uint8_t> dataRomBanks = { _dataRomBanks, 3 };
s.Stream(
_directoryBase, _directoryIndex, _targetOffset, _dataLengthCounter, _skipBytes, _decompFlags, _decompMode, _srcAddress, _decompOffset, _decompStatus,
decompBuffer, _dividend = 0, _multiplier, _divisor, _multDivResult, _remainder, _aluState, _aluFlags, _sramEnabled, dataRomBanks, _dataRomSize, _readBase,
_readOffset, _readStep, _readMode, _readBuffer
);
s.Stream(_decomp.get());
if(_rtc) {
s.Stream(_rtc.get());
}
}
uint8_t Spc7110::Read(uint32_t addr)
{
if((addr & 0xFF0000) == 0x500000) {
addr = 0x4800;
} else if((addr & 0xFF0000) == 0x580000) {
addr = 0x4808;
}
switch(addr & 0xFFFF) {
//Decompression
case 0x4800:
_dataLengthCounter--;
return ReadDecompressedByte();
case 0x4801: return (_directoryBase) & 0xFF;
case 0x4802: return (_directoryBase >> 8) & 0xFF;
case 0x4803: return (_directoryBase >> 16) & 0xFF;
case 0x4804: return _directoryIndex;
case 0x4805: return (_targetOffset) & 0xFF;
case 0x4806: return (_targetOffset >> 8) & 0xFF;
case 0x4807: return _skipBytes;
case 0x4808: return 0;
case 0x4809: return (_dataLengthCounter & 0xFF);
case 0x480A: return (_dataLengthCounter >> 8) & 0xFF;
case 0x480B: return _decompFlags;
case 0x480C: return _decompStatus;
//Data read
case 0x4810: {
uint8_t value = _readBuffer;
IncrementPosition4810();
return value;
}
case 0x4811: return _readBase & 0xFF;
case 0x4812: return (_readBase >> 8) & 0xFF;
case 0x4813: return (_readBase >> 16) & 0xFF;
case 0x4814: return _readOffset & 0xFF;
case 0x4815: return (_readOffset >> 8) & 0xFF;
case 0x4816: return _readStep & 0xFF;
case 0x4817: return (_readStep >> 8) & 0xFF;
case 0x4818: return _readMode;
case 0x481A:
if((_readMode & 0x60) == 0x60) {
IncrementPosition();
}
return 0;
//ALU
case 0x4820: return _dividend & 0xFF;
case 0x4821: return (_dividend >> 8) & 0xFF;
case 0x4822: return (_dividend >> 16) & 0xFF;
case 0x4823: return (_dividend >> 24) & 0xFF;
case 0x4824: return _multiplier & 0xFF;
case 0x4825: return (_multiplier >> 8) & 0xFF;
case 0x4826: return _divisor & 0xFF;
case 0x4827: return (_divisor >> 8) & 0xFF;
case 0x4828: return _multDivResult & 0xFF;
case 0x4829: return (_multDivResult >> 8) & 0xFF;
case 0x482A: return (_multDivResult >> 16) & 0xFF;
case 0x482B: return (_multDivResult >> 24) & 0xFF;
case 0x482C: return _remainder & 0xFF;
case 0x482D: return (_remainder >> 8) & 0xFF;
case 0x482E: return _aluFlags;
case 0x482F: return _aluState;
case 0x4830: return _sramEnabled;
case 0x4831: return _dataRomBanks[0];
case 0x4832: return _dataRomBanks[1];
case 0x4833: return _dataRomBanks[2];
case 0x4834: return _dataRomSize;
case 0x4840:
case 0x4841:
case 0x4842:
return _rtc ? _rtc->Read(addr) : 0;
default:
if((addr & 0xFFFF) >= 0x4800) {
LogDebug("[Debug] Missing read handler: $" + HexUtilities::ToHex(addr & 0xFFFF));
}
return _cpuRegisterHandler->Read(addr);
}
}
void Spc7110::Write(uint32_t addr, uint8_t value)
{
if((addr & 0xFF0000) == 0x500000) {
addr = 0x4800;
} else if((addr & 0xFF0000) == 0x580000) {
addr = 0x4808;
}
switch(addr & 0xFFFF) {
//Data ROM Decompression (4800-480C)
case 0x4801: _directoryBase = (_directoryBase & 0xFFFF00) | value; break;
case 0x4802: _directoryBase = (_directoryBase & 0xFF00FF) | (value << 8); break;
case 0x4803: _directoryBase = (_directoryBase & 0x00FFFF) | (value << 16); break;
case 0x4804:
_directoryIndex = value;
LoadEntryHeader();
break;
case 0x4805: _targetOffset = (_targetOffset & 0xFF00) | value; break;
case 0x4806:
_targetOffset = (_targetOffset & 0x00FF) | (value << 8);
BeginDecompression();
break;
case 0x4807: _skipBytes = value; break;
case 0x4808: break;
case 0x4809: _dataLengthCounter = (_dataLengthCounter & 0xFF00) | value; break;
case 0x480A: _dataLengthCounter = (_dataLengthCounter & 0x00FF) | (value << 8); break;
case 0x480B: _decompFlags = value & 0x03; break;
//Direct Data ROM Access (4810-481A)
case 0x4811: _readBase = (_readBase & 0xFFFF00) | value; break;
case 0x4812: _readBase = (_readBase & 0xFF00FF) | (value << 8); break;
case 0x4813:
_readBase = (_readBase & 0x00FFFF) | (value << 16);
FillReadBuffer();
break;
case 0x4814:
_readOffset = (_readOffset & 0xFF00) | value;
if((_readMode & 0x60) == 0x20) {
IncrementPosition();
}
break;
case 0x4815:
_readOffset = (_readOffset & 0x00FF) | (value << 8);
if(_readMode & 0x02) {
FillReadBuffer();
}
if((_readMode & 0x60) == 0x40) {
IncrementPosition();
}
break;
case 0x4816: _readStep = (_readStep & 0xFF00) | value; break;
case 0x4817: _readStep = (_readStep & 0x00FF) | (value << 8); break;
case 0x4818:
_readMode = value & 0x7F;
FillReadBuffer();
break;
//ALU (4820-482F)
case 0x4820: _dividend = (_dividend & 0xFFFFFF00) | value; break;
case 0x4821: _dividend = (_dividend & 0xFFFF00FF) | (value << 8); break;
case 0x4822: _dividend = (_dividend & 0xFF00FFFF) | (value << 16); break;
case 0x4823: _dividend = (_dividend & 0x00FFFFFF) | (value << 24); break;
case 0x4824: _multiplier = (_multiplier & 0xFF00) | value; break;
case 0x4825:
_multiplier = (_multiplier & 0x00FF) | (value << 8);
ProcessMultiplication();
break;
case 0x4826: _divisor = (_divisor & 0xFF00) | value; break;
case 0x4827:
_divisor = (_divisor & 0x00FF) | (value << 8);
ProcessDivision();
break;
case 0x482E: _aluFlags = value & 0x01; break;
//Memory mapping (4830-4834)
case 0x4830: _sramEnabled = value & 0x87; break;
case 0x4831: _dataRomBanks[0] = value & 0x07; UpdateMappings(); break;
case 0x4832: _dataRomBanks[1] = value & 0x07; UpdateMappings(); break;
case 0x4833: _dataRomBanks[2] = value & 0x07; UpdateMappings(); break;
case 0x4834: _dataRomSize = value & 0x07; break;
//RTC (4840-4842)
case 0x4840:
case 0x4841:
case 0x4842:
if(_rtc) {
_rtc->Write(addr, value);
}
break;
default:
if((addr & 0xFFFF) >= 0x4800) {
LogDebug("[Debug] Missing write handler: $" + HexUtilities::ToHex(addr & 0xFFFF));
}
_cpuRegisterHandler->Write(addr, value);
break;
}
}
void Spc7110::UpdateMappings()
{
MemoryMappings* mappings = _console->GetMemoryManager()->GetMemoryMappings();
vector<unique_ptr<IMemoryHandler>>& prgRomHandlers = _cart->GetPrgRomHandlers();
uint32_t dataRomSize = _realDataRomSize >> 12;
for(int i = 0; i < 3; i++) {
mappings->RegisterHandler(0xD0 + (i * 0x10), 0xDF + (i * 0x10), 0x0000, 0xFFFF, prgRomHandlers, 0, 0x100 + ((_dataRomBanks[i] * 0x100) % dataRomSize));
}
}
void Spc7110::ProcessMultiplication()
{
if(_aluFlags & 0x01) {
//Signed multiplication (16x16=32)
_multDivResult = (int32_t)((int16_t)_dividend * (int16_t)_multiplier);
} else {
//Unsigned multiplication (16x16=32)
_multDivResult = (uint32_t)(_dividend * _multiplier);
}
_aluState |= 0x01;
_aluState &= 0x7F;
}
void Spc7110::ProcessDivision()
{
if(_aluFlags & 0x01) {
//Signed division (32 / 16 = 32 + 16)
int32_t dividend = (int32_t)_dividend;
int16_t divisor = (int16_t)_divisor;
if(divisor != 0) {
_multDivResult = (int32_t)(dividend / divisor);
_remainder = ((int32_t)(dividend % divisor)) & 0xFFFF;
} else {
_multDivResult = 0;
_remainder = dividend & 0xFFFF;
}
} else {
//Unsigned division (32 / 16 = 32 + 16)
if(_divisor != 0) {
_multDivResult = _dividend / _divisor;
_remainder = (uint16_t)(_dividend % _divisor);
} else {
_multDivResult = 0;
_remainder = _dividend & 0xffff;
}
}
_aluState &= 0x7F;
}
uint8_t Spc7110::ReadDataRom(uint32_t addr)
{
uint32_t configSize = 0x100000 * (1 << (_dataRomSize & 0x03));
uint32_t dataRomSize = std::min(configSize, _realDataRomSize);
if(addr >= dataRomSize) {
return 0x00;
}
return _cart->DebugGetPrgRom()[0x100000 + addr];
}
void Spc7110::FillReadBuffer()
{
int32_t offset = _readMode & 0x02 ? _readOffset : 0;
if(_readMode & 0x08) {
offset = (int16_t)offset;
}
_readBuffer = ReadDataRom(_readBase + offset);
}
void Spc7110::IncrementPosition()
{
_readBase += (_readMode & 0x08) ? (int16_t)_readOffset : _readOffset;
FillReadBuffer();
}
void Spc7110::IncrementPosition4810()
{
int32_t step = _readMode & 0x01 ? _readStep : 1;
if(_readMode & 0x04) {
step = (int16_t)step;
}
if(_readMode & 0x10) {
_readOffset += step;
} else {
_readBase = (_readBase + step) & 0xFFFFFF;
}
FillReadBuffer();
}
void Spc7110::LoadEntryHeader()
{
uint32_t address = _directoryBase + _directoryIndex * 4;
_decompMode = ReadDataRom(address);
_srcAddress = (ReadDataRom(address + 1) << 16) | (ReadDataRom(address + 2) << 8) | ReadDataRom(address + 3);
}
void Spc7110::BeginDecompression()
{
if(_decompMode == 3) {
return;
}
_decomp->Initialize(_decompMode, _srcAddress);
_decomp->Decode();
uint32_t seek = _decompFlags & 0x02 ? _targetOffset : 0;
while(seek--) {
_decomp->Decode();
}
_decompStatus |= 0x80;
_decompOffset = 0;
}
uint8_t Spc7110::ReadDecompressedByte()
{
if((_decompStatus & 0x80) == 0) {
return 0x00;
}
uint8_t bpp = _decomp->GetBpp();
if(_decompOffset == 0) {
for(int i = 0; i < 8; i++) {
uint32_t result = _decomp->GetResult();
switch(bpp) {
case 1:
_decompBuffer[i] = result;
break;
case 2:
_decompBuffer[i * 2 + 0] = result >> 0;
_decompBuffer[i * 2 + 1] = result >> 8;
break;
case 4:
_decompBuffer[i * 2 + 0] = result >> 0;
_decompBuffer[i * 2 + 1] = result >> 8;
_decompBuffer[i * 2 + 16] = result >> 16;
_decompBuffer[i * 2 + 17] = result >> 24;
break;
}
uint32_t seek = (_decompFlags & 0x01) ? _skipBytes : 1;
while(seek--) {
_decomp->Decode();
}
}
}
uint8_t data = _decompBuffer[_decompOffset++];
_decompOffset &= (bpp * 8) - 1;
return data;
}
uint8_t Spc7110::Peek(uint32_t addr)
{
return 0;
}
void Spc7110::PeekBlock(uint32_t addr, uint8_t* output)
{
memset(output, 0, 0x1000);
}
AddressInfo Spc7110::GetAbsoluteAddress(uint32_t address)
{
return { -1, SnesMemoryType::Register };
}
void Spc7110::Reset()
{
_directoryBase = 0;
_directoryIndex = 0;
_targetOffset = 0;
_dataLengthCounter = 0;
_skipBytes = 0;
_decompFlags = 0;
_decompMode = 0;
_srcAddress = 0;
_decompOffset = 0;
_decompStatus = 0;
memset(_decompBuffer, 0, sizeof(_decompBuffer));
_dividend = 0;
_multiplier = 0;
_divisor = 0;
_multDivResult = 0;
_remainder = 0;
_aluState = 0;
_aluFlags = 0;
_sramEnabled = 0;
_dataRomBanks[0] = 0;
_dataRomBanks[1] = 1;
_dataRomBanks[2] = 2;
_dataRomSize = 0;
_readBase = 0;
_readOffset = 0;
_readStep = 0;
_readMode = 0;
_readBuffer = 0;
UpdateMappings();
_decomp.reset(new Spc7110Decomp(this));
if(_useRtc) {
_rtc.reset(new Rtc4513(_console));
}
}
void Spc7110::LoadBattery()
{
if(_rtc) {
_rtc->LoadBattery();
}
}
void Spc7110::SaveBattery()
{
if(_rtc) {
_rtc->SaveBattery();
}
}