Added SPC7110 support
This commit is contained in:
parent
6f22bb5521
commit
b856f615d1
14 changed files with 1115 additions and 16 deletions
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
|
@ -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
185
Core/Rtc4513.cpp
Normal 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
44
Core/Rtc4513.h
Normal 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
485
Core/Spc7110.cpp
Normal 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
82
Core/Spc7110.h
Normal 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
196
Core/Spc7110Decomp.cpp
Normal 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
58
Core/Spc7110Decomp.h
Normal 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;
|
||||
};
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue