6539b92502
Memory handlers did not have the _memoryType value set, which caused some memory accesses to be much faster than expected
491 lines
13 KiB
C++
491 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();
|
|
}
|
|
}
|