Added SPC7110 support

This commit is contained in:
Sour 2020-02-16 21:11:01 -05:00
parent 6f22bb5521
commit b856f615d1
14 changed files with 1115 additions and 16 deletions

View file

@ -16,6 +16,7 @@
#include "Sdd1.h"
#include "Cx4.h"
#include "Obc1.h"
#include "Spc7110.h"
#include "SpcFileData.h"
#include "../Utilities/HexUtilities.h"
#include "../Utilities/VirtualFile.h"
@ -207,19 +208,29 @@ CoprocessorType BaseCartridge::GetCoprocessorType()
{
if((_cartInfo.RomType & 0x0F) >= 0x03) {
switch((_cartInfo.RomType & 0xF0) >> 4) {
case 0x00: return GetDspVersion(); break;
case 0x01: return CoprocessorType::GSU; break;
case 0x02: return CoprocessorType::OBC1; break;
case 0x03: return CoprocessorType::SA1; break;
case 0x04: return CoprocessorType::SDD1; break;
case 0x05: return CoprocessorType::RTC; break;
case 0x0E: return CoprocessorType::Satellaview; break;
case 0x00: return GetDspVersion();
case 0x01: return CoprocessorType::GSU;
case 0x02: return CoprocessorType::OBC1;
case 0x03: return CoprocessorType::SA1;
case 0x04: return CoprocessorType::SDD1;
case 0x05: return CoprocessorType::RTC;
case 0x0E: return CoprocessorType::Satellaview;
case 0x0F:
switch(_cartInfo.CartridgeType) {
case 0x00: return CoprocessorType::SPC7110; _hasBattery = true; break;
case 0x01: return GetSt01xVersion(); _hasBattery = true; break;
case 0x02: return CoprocessorType::ST018; _hasBattery = true; break;
case 0x10: return CoprocessorType::CX4; break;
case 0x00:
_hasBattery = true;
_hasRtc = (_cartInfo.RomType & 0x0F) == 0x09;
return CoprocessorType::SPC7110;
case 0x01:
_hasBattery = true;
return GetSt01xVersion();
case 0x02:
_hasBattery = true;
return CoprocessorType::ST018;
case 0x10: return CoprocessorType::CX4;
}
break;
}
@ -288,7 +299,9 @@ void BaseCartridge::LoadBattery()
{
if(_saveRamSize > 0) {
_console->GetBatteryManager()->LoadBattery(".srm", _saveRam, _saveRamSize);
} else if(_coprocessor && _hasBattery) {
}
if(_coprocessor && _hasBattery) {
_coprocessor->LoadBattery();
}
}
@ -297,7 +310,9 @@ void BaseCartridge::SaveBattery()
{
if(_saveRamSize > 0) {
_console->GetBatteryManager()->SaveBattery(".srm", _saveRam, _saveRamSize);
} else if(_coprocessor && _hasBattery) {
}
if(_coprocessor && _hasBattery) {
_coprocessor->SaveBattery();
}
}
@ -333,7 +348,7 @@ void BaseCartridge::Init(MemoryMappings &mm)
void BaseCartridge::RegisterHandlers(MemoryMappings &mm)
{
if(MapSpecificCarts(mm) || _coprocessorType == CoprocessorType::GSU || _coprocessorType == CoprocessorType::SDD1 || _coprocessorType == CoprocessorType::CX4) {
if(MapSpecificCarts(mm) || _coprocessorType == CoprocessorType::GSU || _coprocessorType == CoprocessorType::SDD1 || _coprocessorType == CoprocessorType::SPC7110 || _coprocessorType == CoprocessorType::CX4) {
return;
}
@ -406,6 +421,8 @@ void BaseCartridge::InitCoprocessor()
_gsu = dynamic_cast<Gsu*>(_coprocessor.get());
} else if(_coprocessorType == CoprocessorType::SDD1) {
_coprocessor.reset(new Sdd1(_console));
} else if(_coprocessorType == CoprocessorType::SPC7110) {
_coprocessor.reset(new Spc7110(_console, _hasRtc));
} else if(_coprocessorType == CoprocessorType::CX4) {
_coprocessor.reset(new Cx4(_console));
_cx4 = dynamic_cast<Cx4*>(_coprocessor.get());

View file

@ -35,6 +35,7 @@ private:
CartFlags::CartFlags _flags = CartFlags::CartFlags::None;
CoprocessorType _coprocessorType = CoprocessorType::None;
bool _hasBattery = false;
bool _hasRtc = false;
string _romPath;
string _patchPath;

View file

@ -168,6 +168,7 @@
<ClInclude Include="RewindManager.h" />
<ClInclude Include="RomFinder.h" />
<ClInclude Include="RomHandler.h" />
<ClInclude Include="Rtc4513.h" />
<ClInclude Include="Sa1.h" />
<ClInclude Include="Sa1BwRamHandler.h" />
<ClInclude Include="Sa1Cpu.h" />
@ -193,6 +194,8 @@
<ClInclude Include="SoundMixer.h" />
<ClInclude Include="SoundResampler.h" />
<ClInclude Include="Spc.h" />
<ClInclude Include="Spc7110.h" />
<ClInclude Include="Spc7110Decomp.h" />
<ClInclude Include="SpcDebugger.h" />
<ClInclude Include="SpcDisUtils.h" />
<ClInclude Include="SpcHud.h" />
@ -281,6 +284,7 @@
<ClCompile Include="RegisterHandlerB.cpp" />
<ClCompile Include="RewindData.cpp" />
<ClCompile Include="RewindManager.cpp" />
<ClCompile Include="Rtc4513.cpp" />
<ClCompile Include="Sa1.cpp" />
<ClCompile Include="Sa1Cpu.cpp" />
<ClCompile Include="SaveStateManager.cpp" />
@ -297,6 +301,8 @@
<ClCompile Include="SoundResampler.cpp" />
<ClCompile Include="Spc.cpp" />
<ClCompile Include="Spc.Instructions.cpp" />
<ClCompile Include="Spc7110.cpp" />
<ClCompile Include="Spc7110Decomp.cpp" />
<ClCompile Include="SpcDebugger.cpp" />
<ClCompile Include="SpcDisUtils.cpp" />
<ClCompile Include="SpcHud.cpp" />

View file

@ -494,6 +494,15 @@
<ClInclude Include="Assembler.h">
<Filter>Debugger</Filter>
</ClInclude>
<ClInclude Include="Spc7110.h">
<Filter>SNES\Coprocessors\SPC7110</Filter>
</ClInclude>
<ClInclude Include="Spc7110Decomp.h">
<Filter>SNES\Coprocessors\SPC7110</Filter>
</ClInclude>
<ClInclude Include="Rtc4513.h">
<Filter>SNES\Coprocessors\SPC7110</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -785,6 +794,15 @@
<ClCompile Include="Assembler.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="Spc7110.cpp">
<Filter>SNES\Coprocessors\SPC7110</Filter>
</ClCompile>
<ClCompile Include="Spc7110Decomp.cpp">
<Filter>SNES\Coprocessors\SPC7110</Filter>
</ClCompile>
<ClCompile Include="Rtc4513.cpp">
<Filter>SNES\Coprocessors\SPC7110</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">
@ -856,5 +874,8 @@
<Filter Include="SNES\Coprocessors\MSU1">
<UniqueIdentifier>{9fb8db1f-a7b5-4570-99ca-bbc15d262632}</UniqueIdentifier>
</Filter>
<Filter Include="SNES\Coprocessors\SPC7110">
<UniqueIdentifier>{996b46fc-8c11-4715-99a0-394640d3fdcf}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View file

@ -617,6 +617,7 @@ int LuaApi::GetAccessCounters(lua_State *lua)
lua_newtable(lua);
switch(operationType) {
default:
case MemoryOperationType::Read:
for(uint32_t i = 0; i < size; i++) {
lua_pushinteger(lua, counts[i].ReadCount);

185
Core/Rtc4513.cpp Normal file
View file

@ -0,0 +1,185 @@
#include "stdafx.h"
#include <time.h>
#include "Rtc4513.h"
#include "Console.h"
#include "MessageManager.h"
#include "BatteryManager.h"
#include "../Utilities/HexUtilities.h"
//TODO: Partial implementation
//Missing stuff: most flags e.g: 30ADJ, 24/12, CAL/HW, WRAP, etc.
Rtc4513::Rtc4513(Console* console)
{
_console = console;
}
Rtc4513::~Rtc4513()
{
}
void Rtc4513::LoadBattery()
{
vector<uint8_t> rtcData = _console->GetBatteryManager()->LoadBattery(".rtc");
if(rtcData.size() == sizeof(_regs) + sizeof(uint64_t)) {
memcpy(_regs, rtcData.data(), sizeof(_regs));
uint64_t time = 0;
for(uint32_t i = 0; i < sizeof(uint64_t); i++) {
time <<= 8;
time |= rtcData[sizeof(_regs) + i];
}
_lastTime = time;
} else {
_lastTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
}
void Rtc4513::SaveBattery()
{
vector<uint8_t> rtcData;
rtcData.resize(sizeof(_regs) + sizeof(uint64_t), 0);
memcpy(rtcData.data(), _regs, sizeof(_regs));
uint64_t time = _lastTime;
for(uint32_t i = 0; i < sizeof(uint64_t); i++) {
rtcData[sizeof(_regs) + i] = (time >> 56) & 0xFF;
time <<= 8;
}
_console->GetBatteryManager()->SaveBattery(".rtc", rtcData.data(), (uint32_t)rtcData.size());
}
void Rtc4513::UpdateTime()
{
if(IsReset()) {
//Reset seconds to 0
_regs[0] = 0;
_regs[1] = 0;
}
uint64_t currentTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
uint32_t elapsedSeconds = (uint32_t)(currentTime - _lastTime);
if(elapsedSeconds <= 0) {
return;
}
if(IsStop() || IsReset() || IsHold()) {
_lastTime = currentTime;
return;
}
std::tm tm = { };
tm.tm_sec = GetSeconds();
tm.tm_min = GetMinutes();
tm.tm_hour = GetHours();
tm.tm_mday = GetDay();
tm.tm_mon = GetMonth() - 1;
tm.tm_year = (GetYear() >= 90 ? 0 : 100) + GetYear();
std::time_t tt = mktime(&tm);
if(tt == -1) {
_lastTime = currentTime;
return;
}
std::chrono::system_clock::time_point timePoint = std::chrono::system_clock::from_time_t(tt);
timePoint += std::chrono::seconds((uint32_t)elapsedSeconds);
std::time_t newTime = system_clock::to_time_t(timePoint);
std::tm newTm;
#ifdef _MSC_VER
localtime_s(&newTm, &newTime);
#else
localtime_r(&newTime, &newTm);
#endif
_regs[0] = newTm.tm_sec % 10;
_regs[1] = newTm.tm_sec / 10;
_regs[2] = newTm.tm_min % 10;
_regs[3] = newTm.tm_min / 10;
_regs[4] = newTm.tm_hour % 10;
_regs[5] = newTm.tm_hour / 10;
_regs[6] = newTm.tm_mday % 10;
_regs[7] = newTm.tm_mday / 10;
_regs[8] = (newTm.tm_mon + 1) % 10;
_regs[9] = (newTm.tm_mon + 1) / 10;
int year = newTm.tm_year + 1900;
year -= year >= 2000 ? 2000 : 1900;
_regs[10] = year % 10;
_regs[11] = year / 10;
_regs[12] = (newTm.tm_wday + 1) % 7;
_lastTime = currentTime;
}
uint8_t Rtc4513::Read(uint16_t addr)
{
UpdateTime();
switch(addr) {
case 0x4840: break;
case 0x4841:
if(_mode == 0x0C) {
//Read mode
//LogDebug("Read: " + HexUtilities::ToHex(_index) + " = " + HexUtilities::ToHex(_regs[_index]));
uint8_t index = _index;
_index = (_index + 1) & 0x0F;
return _regs[index];
}
break;
case 0x4842:
//Ready
return 0x80;
}
return 0;
}
void Rtc4513::Write(uint16_t addr, uint8_t value)
{
UpdateTime();
switch(addr) {
case 0x4840:
_enabled = value;
if(!(_enabled & 0x01)) {
_mode = -1;
_index = -1;
//Turn off reset ($01) and test ($08) bits when disabled
_regs[0x0F] &= 0x06;
}
break;
case 0x4841:
if(_mode == -1) {
_mode = value & 0x0F;
} else if(_index == -1) {
_index = value & 0x0F;
} else if(_mode == 0x03) {
//Write mode
//LogDebug(HexUtilities::ToHex(_index) + " = " + HexUtilities::ToHex(value & 0x0F));
uint8_t index = _index;
_index = (_index + 1) & 0x0F;
_regs[index] = value & 0x0F;
}
case 0x4842: break;
}
}
void Rtc4513::Serialize(Serializer& s)
{
ArrayInfo<uint8_t> regs = { _regs, 0x10 };
s.Stream(_lastTime, _enabled, _mode, _index, regs);
}

44
Core/Rtc4513.h Normal file
View file

@ -0,0 +1,44 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/ISerializable.h"
class Console;
class Rtc4513 : public ISerializable
{
private:
Console* _console;
uint64_t _lastTime = 0;
uint8_t _enabled = 0;
int8_t _mode = -1;
int8_t _index = -1;
uint8_t _regs[0x10] = {};
bool IsReset() { return (_regs[0xF] & 0x01) != 0; }
bool IsStop() { return (_regs[0xF] & 0x02) != 0; }
bool IsHold() { return (_regs[0xD] & 0x01) != 0; }
uint8_t GetSeconds() { return _regs[0] + ((_regs[1] & 0x07) * 10); }
uint8_t GetMinutes() { return _regs[2] + ((_regs[3] & 0x07) * 10); }
uint8_t GetHours() { return _regs[4] + ((_regs[5] & 0x03) * 10); }
uint8_t GetDay() { return _regs[6] + ((_regs[7] & 0x03) * 10); }
uint8_t GetMonth() { return _regs[8] + ((_regs[9] & 0x01) * 10); }
uint8_t GetYear() { return _regs[10] + (_regs[11] * 10); }
uint8_t GetDoW() { return _regs[12] & 0x07; }
void UpdateTime();
public:
Rtc4513(Console* console);
virtual ~Rtc4513();
void LoadBattery();
void SaveBattery();
uint8_t Read(uint16_t addr);
void Write(uint16_t addr, uint8_t value);
void Serialize(Serializer& s) override;
};

485
Core/Spc7110.cpp Normal file
View file

@ -0,0 +1,485 @@
#include "stdafx.h"
#include "Spc7110.h"
#include "Spc7110Decomp.h"
#include "Console.h"
#include "MemoryMappings.h"
#include "BaseCartridge.h"
#include "MemoryManager.h"
#include "BatteryManager.h"
#include "MessageManager.h"
#include "../Utilities/HexUtilities.h"
Spc7110::Spc7110(Console* console, bool useRtc)
{
_console = console;
_useRtc = useRtc;
MemoryMappings* mappings = console->GetMemoryManager()->GetMemoryMappings();
vector<unique_ptr<IMemoryHandler>>& prgRomHandlers = console->GetCartridge()->GetPrgRomHandlers();
vector<unique_ptr<IMemoryHandler>>& saveRamHandlers = console->GetCartridge()->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);
mappings->RegisterHandler(0x40, 0x4F, 0x0000, 0xFFFF, prgRomHandlers);
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 = _console->GetCartridge()->GetPrgRomHandlers();
uint32_t dataRomSize = (prgRomHandlers.size() - 0x100);
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 size = 1 << (_dataRomSize & 0x03);
uint32_t mask = 0x100000 * size - 1;
if((_dataRomSize & 0x03) != 0x03 && (addr & 0x400000)) {
return 0x00;
}
uint32_t realDataRomSize = _console->GetCartridge()->DebugGetPrgRomSize() - 0x100000;
mask = std::min(mask, realDataRomSize - 1);
return _console->GetCartridge()->DebugGetPrgRom()[0x100000 + (addr & mask)];
}
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();
}
}

82
Core/Spc7110.h Normal file
View file

@ -0,0 +1,82 @@
#pragma once
#include "stdafx.h"
#include "BaseCoprocessor.h"
#include "Spc7110Decomp.h"
#include "Rtc4513.h"
class Console;
class Spc7110Decomp;
class IMemoryHandler;
class Spc7110 : public BaseCoprocessor
{
private:
unique_ptr<Spc7110Decomp> _decomp;
unique_ptr<Rtc4513> _rtc;
IMemoryHandler* _cpuRegisterHandler = nullptr;
Console* _console = nullptr;
bool _useRtc = false;
//Decomp
uint32_t _directoryBase = 0;
uint8_t _directoryIndex = 0;
uint16_t _targetOffset = 0;
uint16_t _dataLengthCounter = 0;
uint8_t _skipBytes = 0;
uint8_t _decompFlags = 0;
uint8_t _decompMode = 0;
uint32_t _srcAddress = 0;
uint32_t _decompOffset = 0;
uint8_t _decompStatus = 0;
uint8_t _decompBuffer[32];
//ALU
uint32_t _dividend = 0;
uint16_t _multiplier = 0;
uint16_t _divisor = 0;
uint32_t _multDivResult = 0;
uint16_t _remainder = 0;
uint8_t _aluState = 0;
uint8_t _aluFlags = 0;
//Memory mappings
uint8_t _sramEnabled = 0;
uint8_t _dataRomBanks[3] = { 0, 1, 2 };
uint8_t _dataRomSize = 0;
//Data rom
uint32_t _readBase = 0;
uint16_t _readOffset = 0;
uint16_t _readStep = 0;
uint8_t _readMode = 0;
uint8_t _readBuffer = 0;
void ProcessMultiplication();
void ProcessDivision();
void FillReadBuffer();
void IncrementPosition();
void IncrementPosition4810();
void LoadEntryHeader();
void BeginDecompression();
uint8_t ReadDecompressedByte();
public:
Spc7110(Console* console, bool useRtc);
uint8_t ReadDataRom(uint32_t addr);
void Serialize(Serializer& s) override;
uint8_t Read(uint32_t addr) override;
uint8_t Peek(uint32_t addr) override;
void PeekBlock(uint32_t addr, uint8_t* output) override;
void Write(uint32_t addr, uint8_t value) override;
void UpdateMappings();
AddressInfo GetAbsoluteAddress(uint32_t address) override;
void Reset() override;
void LoadBattery() override;
void SaveBattery() override;
};

196
Core/Spc7110Decomp.cpp Normal file
View file

@ -0,0 +1,196 @@
#include "stdafx.h"
#include "Spc7110Decomp.h"
#include "Spc7110.h"
//Based on bsnes' code (by byuu)
//original implementation: neviksti
//optimized implementation: talarubi
Spc7110Decomp::Spc7110Decomp(Spc7110* spc)
{
_spc = spc;
}
Spc7110Decomp::~Spc7110Decomp()
{
}
uint8_t Spc7110Decomp::ReadByte()
{
return _spc->ReadDataRom(_offset++);
}
//inverse morton code transform: unpack big-endian packed pixels
//returns odd bits in lower half; even bits in upper half
uint32_t Spc7110Decomp::Deinterleave(uint64_t data, uint32_t bits)
{
data = data & ((1ull << bits) - 1);
data = 0x5555555555555555ull & (data << bits | data >> 1);
data = 0x3333333333333333ull & (data | data >> 1);
data = 0x0f0f0f0f0f0f0f0full & (data | data >> 2);
data = 0x00ff00ff00ff00ffull & (data | data >> 4);
data = 0x0000ffff0000ffffull & (data | data >> 8);
return (uint32_t)(data | data >> 16);
}
//extract a nibble and move it to the low four bits
uint64_t Spc7110Decomp::MoveToFront(uint64_t list, uint32_t nibble)
{
for(uint64_t n = 0, mask = ~15; n < 64; n += 4, mask <<= 4) {
if((list >> n & 15) != nibble) {
continue;
}
return (list & mask) + (list << 4 & ~mask) + nibble;
}
return list;
}
void Spc7110Decomp::Initialize(uint32_t mode, uint32_t origin)
{
memset(_context, 0, sizeof(_context));
_bpp = 1 << mode;
_offset = origin;
_bits = 8;
_range = Max + 1;
_input = ReadByte();
_input = _input << 8 | ReadByte();
_output = 0;
_pixels = 0;
_colormap = 0xfedcba9876543210ull;
}
void Spc7110Decomp::Decode()
{
for(uint32_t pixel = 0; pixel < 8; pixel++) {
uint64_t map = _colormap;
uint32_t diff = 0;
if(_bpp > 1) {
uint32_t pa = (_bpp == 2 ? (_pixels >> 2) & 3 : (_pixels >> 0) & 15);
uint32_t pb = (_bpp == 2 ? (_pixels >> 14) & 3 : (_pixels >> 28) & 15);
uint32_t pc = (_bpp == 2 ? (_pixels >> 16) & 3 : (_pixels >> 32) & 15);
if(pa != pb || pb != pc) {
uint32_t match = pa ^ pb ^ pc;
diff = 4; //no match; all pixels differ
if((match ^ pc) == 0) diff = 3; //a == b; pixel c differs
if((match ^ pb) == 0) diff = 2; //c == a; pixel b differs
if((match ^ pa) == 0) diff = 1; //b == c; pixel a differs
}
_colormap = MoveToFront(_colormap, pa);
map = MoveToFront(map, pc);
map = MoveToFront(map, pb);
map = MoveToFront(map, pa);
}
for(uint32_t plane = 0; plane < _bpp; plane++) {
uint32_t bit = _bpp > 1 ? 1 << plane : 1 << (pixel & 3);
uint32_t history = (bit - 1) & _output;
uint32_t set = 0;
if(_bpp == 1) {
set = pixel >= 4;
} else if(_bpp == 2) {
set = diff;
}
if(plane >= 2 && history <= 1) {
set = diff;
}
auto& ctx = _context[set][bit + history - 1];
auto& model = evolution[ctx.prediction];
uint8_t lps_offset = _range - model.probability;
bool symbol = _input >= (lps_offset << 8); //test only the MSB
_output = _output << 1 | ((uint8_t)symbol ^ ctx.swap);
if(symbol == MPS) { //[0 ... range-p]
_range = lps_offset; //range = range-p
} else { //[range-p+1 ... range]
_range -= lps_offset; //range = p-1, with p < 0.75
_input -= lps_offset << 8; //therefore, always rescale
}
while(_range <= Max / 2) { //scale back into [0.75 ... 1.5]
ctx.prediction = model.next[symbol];
_range <<= 1;
_input <<= 1;
if(--_bits == 0) {
_bits = 8;
_input += ReadByte();
}
}
if(symbol == LPS && model.probability > Half) {
ctx.swap ^= 1;
}
}
uint32_t index = _output & ((1 << _bpp) - 1);
if(_bpp == 1) {
index ^= _pixels >> 15 & 1;
}
_pixels = _pixels << _bpp | (map >> 4 * index & 15);
}
if(_bpp == 1) {
_result = (uint32_t)_pixels;
} else if(_bpp == 2) {
_result = Deinterleave(_pixels, 16);
} else if(_bpp == 4) {
_result = Deinterleave(Deinterleave(_pixels, 32), 32);
}
}
uint32_t Spc7110Decomp::GetResult()
{
return _result;
}
uint8_t Spc7110Decomp::GetBpp()
{
return _bpp;
}
void Spc7110Decomp::Serialize(Serializer& s)
{
s.Stream(_bpp, _offset, _bits, _range, _input, _output, _pixels, _colormap, _result);
for(int i = 0; i < 5; i++) {
for(int j = 0; j < 15; j++) {
s.Stream(_context[i][j].swap, _context[i][j].prediction);
}
}
}
Spc7110Decomp::ModelState Spc7110Decomp::evolution[53] = {
{0x5a, { 1, 1}}, {0x25, { 2, 6}}, {0x11, { 3, 8}},
{0x08, { 4,10}}, {0x03, { 5,12}}, {0x01, { 5,15}},
{0x5a, { 7, 7}}, {0x3f, { 8,19}}, {0x2c, { 9,21}},
{0x20, {10,22}}, {0x17, {11,23}}, {0x11, {12,25}},
{0x0c, {13,26}}, {0x09, {14,28}}, {0x07, {15,29}},
{0x05, {16,31}}, {0x04, {17,32}}, {0x03, {18,34}},
{0x02, { 5,35}},
{0x5a, {20,20}}, {0x48, {21,39}}, {0x3a, {22,40}},
{0x2e, {23,42}}, {0x26, {24,44}}, {0x1f, {25,45}},
{0x19, {26,46}}, {0x15, {27,25}}, {0x11, {28,26}},
{0x0e, {29,26}}, {0x0b, {30,27}}, {0x09, {31,28}},
{0x08, {32,29}}, {0x07, {33,30}}, {0x05, {34,31}},
{0x04, {35,33}}, {0x04, {36,33}}, {0x03, {37,34}},
{0x02, {38,35}}, {0x02, { 5,36}},
{0x58, {40,39}}, {0x4d, {41,47}}, {0x43, {42,48}},
{0x3b, {43,49}}, {0x34, {44,50}}, {0x2e, {45,51}},
{0x29, {46,44}}, {0x25, {24,45}},
{0x56, {48,47}}, {0x4f, {49,47}}, {0x47, {50,48}},
{0x41, {51,49}}, {0x3c, {52,50}}, {0x37, {43,51}},
};

58
Core/Spc7110Decomp.h Normal file
View file

@ -0,0 +1,58 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/ISerializable.h"
//Based on bsnes' code (by byuu)
//original implementation: neviksti
//optimized implementation: talarubi
class Spc7110;
class Spc7110Decomp : public ISerializable
{
private:
enum : uint32_t { MPS = 0, LPS = 1 };
enum : uint32_t { One = 0xAA, Half = 0x55, Max = 0xFF };
struct ModelState
{
uint8_t probability; //of the more probable symbol (MPS)
uint8_t next[2]; //next state after output {MPS, LPS}
};
static ModelState evolution[53];
struct Context
{
uint8_t prediction; //current model state
uint8_t swap; //if 1, exchange the role of MPS and LPS
};
Context _context[5][15]; //not all 75 contexts exists; this simplifies the code
Spc7110* _spc;
uint32_t _bpp; //bits per pixel (1bpp = 1; 2bpp = 2; 4bpp = 4)
uint32_t _offset; //SPC7110 data ROM read offset
uint32_t _bits; //bits remaining in input
uint16_t _range; //arithmetic range: technically 8-bits, but Max+1 = 256
uint16_t _input; //input data from SPC7110 data ROM
uint8_t _output;
uint64_t _pixels;
uint64_t _colormap; //most recently used list
uint32_t _result; //decompressed word after calling decode()
private:
uint8_t ReadByte();
uint32_t Deinterleave(uint64_t data, uint32_t bits);
uint64_t MoveToFront(uint64_t list, uint32_t nibble);
public:
Spc7110Decomp(Spc7110* spc);
virtual ~Spc7110Decomp();
void Initialize(uint32_t mode, uint32_t origin);
void Decode();
uint32_t GetResult();
uint8_t GetBpp();
void Serialize(Serializer& s) override;
};

View file

@ -87,6 +87,7 @@ SOURCES_CXX := $(LIBRETRO_DIR)/libretro.cpp \
$(CORE_DIR)/RegisterHandlerB.cpp \
$(CORE_DIR)/RewindData.cpp \
$(CORE_DIR)/RewindManager.cpp \
$(CORE_DIR)/Rtc4513.cpp \
$(CORE_DIR)/SaveStateManager.cpp \
$(CORE_DIR)/Sa1.cpp \
$(CORE_DIR)/Sa1Cpu.cpp \
@ -108,6 +109,8 @@ SOURCES_CXX := $(LIBRETRO_DIR)/libretro.cpp \
$(CORE_DIR)/SpcHud.cpp \
$(CORE_DIR)/SPC_DSP.cpp \
$(CORE_DIR)/SPC_Filter.cpp \
$(CORE_DIR)/Spc7110.cpp \
$(CORE_DIR)/Spc7110Decomp.cpp \
$(CORE_DIR)/stdafx.cpp \
$(CORE_DIR)/TraceLogger.cpp \
$(CORE_DIR)/VideoDecoder.cpp \

View file

@ -20,7 +20,6 @@ Linux: [![Build status](https://ci.appveyor.com/api/projects/status/arkaatgy94f2
The following should be added over time (in no particular order):
* Additions/improvements in the debugging tools
* SPC7110 support
* Satellaview/BS-X support
## Compiling

View file

@ -18,8 +18,9 @@ namespace Mesen.GUI.Emulation
public static void UpdateStateMenu(ToolStripMenuItem menu, bool forSave)
{
string romName = EmuApi.GetRomInfo().GetRomName();
for(uint i = 1; i <= NumberOfSaveSlots + (forSave ? 0 : 1); i++) {
string statePath = Path.Combine(ConfigManager.SaveStateFolder, EmuApi.GetRomInfo().GetRomName() + "_" + i + ".mss");
string statePath = Path.Combine(ConfigManager.SaveStateFolder, romName + "_" + i + ".mss");
string label;
bool isAutoSaveSlot = i == NumberOfSaveSlots + 1;
string slotName = isAutoSaveSlot ? "Auto" : i.ToString();