Added S-DD1 support
This commit is contained in:
parent
dffce96ed8
commit
bbb6be6a9f
13 changed files with 928 additions and 6 deletions
|
@ -11,6 +11,7 @@
|
|||
#include "NecDsp.h"
|
||||
#include "Sa1.h"
|
||||
#include "Gsu.h"
|
||||
#include "Sdd1.h"
|
||||
#include "../Utilities/HexUtilities.h"
|
||||
#include "../Utilities/VirtualFile.h"
|
||||
#include "../Utilities/FolderUtilities.h"
|
||||
|
@ -187,7 +188,7 @@ CoprocessorType BaseCartridge::GetCoprocessorType()
|
|||
case 0x01: return CoprocessorType::GSU; break;
|
||||
case 0x02: return CoprocessorType::OBC1; break;
|
||||
case 0x03: return CoprocessorType::SA1; break;
|
||||
case 0x04: return CoprocessorType::DD1; break;
|
||||
case 0x04: return CoprocessorType::SDD1; break;
|
||||
case 0x05: return CoprocessorType::RTC; break;
|
||||
case 0x0E: return CoprocessorType::Satellaview; break;
|
||||
case 0x0F:
|
||||
|
@ -312,7 +313,7 @@ void BaseCartridge::Init(MemoryMappings &mm)
|
|||
|
||||
void BaseCartridge::RegisterHandlers(MemoryMappings &mm)
|
||||
{
|
||||
if(_coprocessorType == CoprocessorType::GSU || MapSpecificCarts(mm)) {
|
||||
if(MapSpecificCarts(mm) || _coprocessorType == CoprocessorType::GSU || _coprocessorType == CoprocessorType::SDD1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -366,6 +367,8 @@ void BaseCartridge::InitCoprocessor()
|
|||
} else if(_coprocessorType == CoprocessorType::GSU) {
|
||||
_coprocessor.reset(new Gsu(_console, _coprocessorRamSize));
|
||||
_gsu = dynamic_cast<Gsu*>(_coprocessor.get());
|
||||
} else if(_coprocessorType == CoprocessorType::SDD1) {
|
||||
_coprocessor.reset(new Sdd1(_console));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -468,7 +471,7 @@ void BaseCartridge::DisplayCartInfo()
|
|||
switch(_coprocessorType) {
|
||||
case CoprocessorType::None: coProcMessage += "<none>"; break;
|
||||
case CoprocessorType::CX4: coProcMessage += "CX4"; break;
|
||||
case CoprocessorType::DD1: coProcMessage += "S-DD1"; break;
|
||||
case CoprocessorType::SDD1: coProcMessage += "S-DD1"; break;
|
||||
case CoprocessorType::DSP1: coProcMessage += "DSP1"; break;
|
||||
case CoprocessorType::DSP1B: coProcMessage += "DSP1B"; break;
|
||||
case CoprocessorType::DSP2: coProcMessage += "DSP2"; break;
|
||||
|
|
|
@ -36,7 +36,7 @@ enum class CoprocessorType
|
|||
GSU,
|
||||
OBC1,
|
||||
SA1,
|
||||
DD1,
|
||||
SDD1,
|
||||
RTC,
|
||||
Satellaview,
|
||||
SPC7110,
|
||||
|
|
|
@ -142,6 +142,10 @@
|
|||
<ClInclude Include="ScriptHost.h" />
|
||||
<ClInclude Include="ScriptingContext.h" />
|
||||
<ClInclude Include="ScriptManager.h" />
|
||||
<ClInclude Include="Sdd1.h" />
|
||||
<ClInclude Include="Sdd1Decomp.h" />
|
||||
<ClInclude Include="Sdd1Mmc.h" />
|
||||
<ClInclude Include="Sdd1Types.h" />
|
||||
<ClInclude Include="SettingTypes.h" />
|
||||
<ClInclude Include="ShortcutKeyHandler.h" />
|
||||
<ClInclude Include="SnesController.h" />
|
||||
|
@ -221,6 +225,9 @@
|
|||
<ClCompile Include="ScriptHost.cpp" />
|
||||
<ClCompile Include="ScriptingContext.cpp" />
|
||||
<ClCompile Include="ScriptManager.cpp" />
|
||||
<ClCompile Include="Sdd1.cpp" />
|
||||
<ClCompile Include="Sdd1Decomp.cpp" />
|
||||
<ClCompile Include="Sdd1Mmc.cpp" />
|
||||
<ClCompile Include="ShortcutKeyHandler.cpp" />
|
||||
<ClCompile Include="SoundMixer.cpp" />
|
||||
<ClCompile Include="SoundResampler.cpp" />
|
||||
|
|
|
@ -353,6 +353,18 @@
|
|||
<ClInclude Include="GsuRamHandler.h">
|
||||
<Filter>SNES\Coprocessors\GSU</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Sdd1.h">
|
||||
<Filter>SNES\Coprocessors\SDD1</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Sdd1Mmc.h">
|
||||
<Filter>SNES\Coprocessors\SDD1</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Sdd1Types.h">
|
||||
<Filter>SNES\Coprocessors\SDD1</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Sdd1Decomp.h">
|
||||
<Filter>SNES\Coprocessors\SDD1</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp" />
|
||||
|
@ -569,6 +581,15 @@
|
|||
<ClCompile Include="GsuDisUtils.cpp">
|
||||
<Filter>Debugger\Disassembler</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Sdd1.cpp">
|
||||
<Filter>SNES\Coprocessors\SDD1</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Sdd1Mmc.cpp">
|
||||
<Filter>SNES\Coprocessors\SDD1</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Sdd1Decomp.cpp">
|
||||
<Filter>SNES\Coprocessors\SDD1</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="SNES">
|
||||
|
@ -616,5 +637,8 @@
|
|||
<Filter Include="SNES\Coprocessors\GSU">
|
||||
<UniqueIdentifier>{5bda57b5-9204-46f4-85f5-3b550c589a18}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="SNES\Coprocessors\SDD1">
|
||||
<UniqueIdentifier>{991e1865-b4f2-4066-8b10-d7458e636e6e}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -145,7 +145,7 @@ uint8_t InternalRegisters::Read(uint16_t addr)
|
|||
|
||||
default:
|
||||
LogDebug("[Debug] Unimplemented register read: " + HexUtilities::ToHex(addr));
|
||||
return 0;
|
||||
return _memoryManager->GetOpenBus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
119
Core/Sdd1.cpp
Normal file
119
Core/Sdd1.cpp
Normal file
|
@ -0,0 +1,119 @@
|
|||
#include "stdafx.h"
|
||||
#include "Sdd1.h"
|
||||
#include "Sdd1Mmc.h"
|
||||
#include "Console.h"
|
||||
#include "BaseCartridge.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "MemoryMappings.h"
|
||||
|
||||
Sdd1::Sdd1(Console* console)
|
||||
{
|
||||
//This handler is used to dynamically map the ROM based on the banking registers
|
||||
_sdd1Mmc.reset(new Sdd1Mmc(_state, console->GetCartridge().get()));
|
||||
|
||||
MemoryMappings *cpuMappings = 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 = cpuMappings->GetHandler(0x4000);
|
||||
|
||||
//Based on forum thread info: https://forums.nesdev.com/viewtopic.php?f=12&t=14156
|
||||
cpuMappings->RegisterHandler(0x00, 0x3F, 0x6000, 0x7FFF, saveRamHandlers);
|
||||
cpuMappings->RegisterHandler(0x80, 0xBF, 0x6000, 0x7FFF, saveRamHandlers);
|
||||
cpuMappings->RegisterHandler(0x70, 0x73, 0x0000, 0xFFFF, saveRamHandlers);
|
||||
|
||||
//S-DD1 registers (0x4800-0x4807)
|
||||
cpuMappings->RegisterHandler(0x00, 0x3F, 0x4000, 0x4FFF, this);
|
||||
cpuMappings->RegisterHandler(0x80, 0xBF, 0x4000, 0x4FFF, this);
|
||||
|
||||
cpuMappings->RegisterHandler(0x00, 0x3F, 0x8000, 0xFFFF, prgRomHandlers);
|
||||
cpuMappings->RegisterHandler(0x80, 0xBF, 0x8000, 0xFFFF, prgRomHandlers);
|
||||
|
||||
//Override these segments to implement the miroring that $4805/$4807 cause
|
||||
cpuMappings->RegisterHandler(0x20, 0x3F, 0x8000, 0xFFFF, _sdd1Mmc.get());
|
||||
cpuMappings->RegisterHandler(0xA0, 0xBF, 0x8000, 0xFFFF, _sdd1Mmc.get());
|
||||
|
||||
//Regular bank switched mappings
|
||||
cpuMappings->RegisterHandler(0xC0, 0xFF, 0x0000, 0xFFFF, _sdd1Mmc.get());
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Sdd1::Reset()
|
||||
{
|
||||
_state = {};
|
||||
_state.NeedInit = true;
|
||||
_state.SelectedBanks[0] = 0;
|
||||
_state.SelectedBanks[1] = 1;
|
||||
_state.SelectedBanks[2] = 2;
|
||||
_state.SelectedBanks[3] = 3;
|
||||
}
|
||||
|
||||
uint8_t Sdd1::Read(uint32_t addr)
|
||||
{
|
||||
if((uint16_t)addr >= 0x4800 && (uint16_t)addr <= 0x4807) {
|
||||
switch(addr & 0x07) {
|
||||
case 0: return _state.AllowDmaProcessing;
|
||||
case 1: return _state.ProcessNextDma;
|
||||
|
||||
case 4: case 5: case 6: case 7:
|
||||
return _state.SelectedBanks[addr & 0x03];
|
||||
}
|
||||
}
|
||||
|
||||
return _cpuRegisterHandler->Read(addr);
|
||||
}
|
||||
|
||||
void Sdd1::Write(uint32_t addr, uint8_t value)
|
||||
{
|
||||
if((uint16_t)addr >= 0x4800 && (uint16_t)addr <= 0x4807) {
|
||||
//S-DD1 registers
|
||||
switch(addr & 0x07) {
|
||||
case 0: _state.AllowDmaProcessing = value; break;
|
||||
case 1: _state.ProcessNextDma = value; break;
|
||||
|
||||
case 4: case 5: case 6: case 7:
|
||||
_state.SelectedBanks[addr & 0x03] = value;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if((uint16_t)addr >= 0x4300 && (uint16_t)addr <= 0x437A) {
|
||||
//Keep track of writes to the DMA controller to know which address/length the DMAs will use
|
||||
uint8_t ch = (addr >> 4) & 0x07;
|
||||
switch(addr & 0x0F) {
|
||||
case 0x02: _state.DmaAddress[ch] = (_state.DmaAddress[ch] & 0xFFFF00) | value; break;
|
||||
case 0x03: _state.DmaAddress[ch] = (_state.DmaAddress[ch] & 0xFF00FF) | (value << 8); break;
|
||||
case 0x04: _state.DmaAddress[ch] = (_state.DmaAddress[ch] & 0x00FFFF) | (value << 16); break;
|
||||
case 0x05: _state.DmaLength[ch] = (_state.DmaLength[ch] & 0xFF00) | value; break;
|
||||
case 0x06: _state.DmaLength[ch] = (_state.DmaLength[ch] & 0x00FF) | (value << 8); break;
|
||||
}
|
||||
}
|
||||
|
||||
//Forward everything else to the regular handler
|
||||
_cpuRegisterHandler->Write(addr, value);
|
||||
}
|
||||
}
|
||||
|
||||
void Sdd1::Serialize(Serializer &s)
|
||||
{
|
||||
s.Stream(_state.AllowDmaProcessing, _state.ProcessNextDma, _state.NeedInit);
|
||||
s.StreamArray(_state.DmaAddress, 8);
|
||||
s.StreamArray(_state.DmaLength, 8);
|
||||
s.StreamArray(_state.SelectedBanks, 4);
|
||||
}
|
||||
|
||||
uint8_t Sdd1::Peek(uint32_t addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Sdd1::PeekBlock(uint8_t* output)
|
||||
{
|
||||
memset(output, 0, 0x1000);
|
||||
}
|
||||
|
||||
AddressInfo Sdd1::GetAbsoluteAddress(uint32_t address)
|
||||
{
|
||||
return { -1, SnesMemoryType::Register };
|
||||
}
|
26
Core/Sdd1.h
Normal file
26
Core/Sdd1.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseCoprocessor.h"
|
||||
#include "Sdd1Types.h"
|
||||
|
||||
class Console;
|
||||
class Sdd1Mmc;
|
||||
|
||||
class Sdd1 : public BaseCoprocessor
|
||||
{
|
||||
private:
|
||||
Sdd1State _state;
|
||||
unique_ptr<Sdd1Mmc> _sdd1Mmc;
|
||||
IMemoryHandler* _cpuRegisterHandler;
|
||||
|
||||
public:
|
||||
Sdd1(Console *console);
|
||||
|
||||
void Serialize(Serializer &s) override;
|
||||
uint8_t Read(uint32_t addr) override;
|
||||
uint8_t Peek(uint32_t addr) override;
|
||||
void PeekBlock(uint8_t* output) override;
|
||||
void Write(uint32_t addr, uint8_t value) override;
|
||||
AddressInfo GetAbsoluteAddress(uint32_t address) override;
|
||||
void Reset() override;
|
||||
};
|
446
Core/Sdd1Decomp.cpp
Normal file
446
Core/Sdd1Decomp.cpp
Normal file
|
@ -0,0 +1,446 @@
|
|||
//Altered to integrate with Mesen-S's code
|
||||
|
||||
/************************************************************************
|
||||
|
||||
S-DD1'algorithm emulation code
|
||||
------------------------------
|
||||
|
||||
Author: Andreas Naive
|
||||
Date: August 2003
|
||||
Last update: October 2004
|
||||
|
||||
This code is Public Domain. There is no copyright holded by the author.
|
||||
Said this, the author wish to explicitly emphasize his inalienable moral rights
|
||||
over this piece of intelectual work and the previous research that made it
|
||||
possible, as recognized by most of the copyright laws around the world.
|
||||
|
||||
This code is provided 'as-is', with no warranty, expressed or implied.
|
||||
No responsability is assumed by the author in connection with it.
|
||||
|
||||
The author is greatly indebted with The Dumper, without whose help and
|
||||
patience providing him with real S-DD1 data the research would have never been
|
||||
possible. He also wish to note that in the very beggining of his research,
|
||||
Neviksti had done some steps in the right direction. By last, the author is
|
||||
indirectly indebted to all the people that worked and contributed in the
|
||||
S-DD1 issue in the past.
|
||||
|
||||
An algorithm's documentation is available as a separate document.
|
||||
The implementation is obvious when the algorithm is
|
||||
understood.
|
||||
|
||||
************************************************************************/
|
||||
#include "stdafx.h"
|
||||
#include "Sdd1Decomp.h"
|
||||
#include "Sdd1Mmc.h"
|
||||
|
||||
void SDD1_IM::prepareDecomp(Sdd1Mmc *mmc, uint32_t readAddr)
|
||||
{
|
||||
_sdd1Mmc = mmc;
|
||||
_readAddr = readAddr;
|
||||
bit_count = 4;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
|
||||
uint8_t SDD1_IM::getCodeword(uint8_t code_len)
|
||||
{
|
||||
uint8_t codeword;
|
||||
|
||||
codeword = _sdd1Mmc->ReadRom(_readAddr) << bit_count;
|
||||
|
||||
++bit_count;
|
||||
|
||||
if(codeword & 0x80) {
|
||||
codeword |= _sdd1Mmc->ReadRom(_readAddr + 1) >> (9 - bit_count);
|
||||
bit_count += code_len;
|
||||
}
|
||||
|
||||
if(bit_count & 0x08) {
|
||||
_readAddr++;
|
||||
bit_count &= 0x07;
|
||||
}
|
||||
|
||||
return codeword;
|
||||
}
|
||||
|
||||
SDD1_GCD::SDD1_GCD(SDD1_IM *associatedIM) :
|
||||
IM(associatedIM)
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void SDD1_GCD::getRunCount(uint8_t code_num, uint8_t *MPScount, bool *LPSind)
|
||||
{
|
||||
|
||||
const uint8_t run_count[] = {
|
||||
0x00, 0x00, 0x01, 0x00, 0x03, 0x01, 0x02, 0x00,
|
||||
0x07, 0x03, 0x05, 0x01, 0x06, 0x02, 0x04, 0x00,
|
||||
0x0f, 0x07, 0x0b, 0x03, 0x0d, 0x05, 0x09, 0x01,
|
||||
0x0e, 0x06, 0x0a, 0x02, 0x0c, 0x04, 0x08, 0x00,
|
||||
0x1f, 0x0f, 0x17, 0x07, 0x1b, 0x0b, 0x13, 0x03,
|
||||
0x1d, 0x0d, 0x15, 0x05, 0x19, 0x09, 0x11, 0x01,
|
||||
0x1e, 0x0e, 0x16, 0x06, 0x1a, 0x0a, 0x12, 0x02,
|
||||
0x1c, 0x0c, 0x14, 0x04, 0x18, 0x08, 0x10, 0x00,
|
||||
0x3f, 0x1f, 0x2f, 0x0f, 0x37, 0x17, 0x27, 0x07,
|
||||
0x3b, 0x1b, 0x2b, 0x0b, 0x33, 0x13, 0x23, 0x03,
|
||||
0x3d, 0x1d, 0x2d, 0x0d, 0x35, 0x15, 0x25, 0x05,
|
||||
0x39, 0x19, 0x29, 0x09, 0x31, 0x11, 0x21, 0x01,
|
||||
0x3e, 0x1e, 0x2e, 0x0e, 0x36, 0x16, 0x26, 0x06,
|
||||
0x3a, 0x1a, 0x2a, 0x0a, 0x32, 0x12, 0x22, 0x02,
|
||||
0x3c, 0x1c, 0x2c, 0x0c, 0x34, 0x14, 0x24, 0x04,
|
||||
0x38, 0x18, 0x28, 0x08, 0x30, 0x10, 0x20, 0x00,
|
||||
0x7f, 0x3f, 0x5f, 0x1f, 0x6f, 0x2f, 0x4f, 0x0f,
|
||||
0x77, 0x37, 0x57, 0x17, 0x67, 0x27, 0x47, 0x07,
|
||||
0x7b, 0x3b, 0x5b, 0x1b, 0x6b, 0x2b, 0x4b, 0x0b,
|
||||
0x73, 0x33, 0x53, 0x13, 0x63, 0x23, 0x43, 0x03,
|
||||
0x7d, 0x3d, 0x5d, 0x1d, 0x6d, 0x2d, 0x4d, 0x0d,
|
||||
0x75, 0x35, 0x55, 0x15, 0x65, 0x25, 0x45, 0x05,
|
||||
0x79, 0x39, 0x59, 0x19, 0x69, 0x29, 0x49, 0x09,
|
||||
0x71, 0x31, 0x51, 0x11, 0x61, 0x21, 0x41, 0x01,
|
||||
0x7e, 0x3e, 0x5e, 0x1e, 0x6e, 0x2e, 0x4e, 0x0e,
|
||||
0x76, 0x36, 0x56, 0x16, 0x66, 0x26, 0x46, 0x06,
|
||||
0x7a, 0x3a, 0x5a, 0x1a, 0x6a, 0x2a, 0x4a, 0x0a,
|
||||
0x72, 0x32, 0x52, 0x12, 0x62, 0x22, 0x42, 0x02,
|
||||
0x7c, 0x3c, 0x5c, 0x1c, 0x6c, 0x2c, 0x4c, 0x0c,
|
||||
0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04,
|
||||
0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08,
|
||||
0x70, 0x30, 0x50, 0x10, 0x60, 0x20, 0x40, 0x00,
|
||||
};
|
||||
|
||||
uint8_t codeword = IM->getCodeword(code_num);
|
||||
|
||||
if(codeword & 0x80) {
|
||||
*LPSind = 1;
|
||||
*MPScount = run_count[codeword >> (code_num ^ 0x07)];
|
||||
} else {
|
||||
*MPScount = (1 << code_num);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
SDD1_BG::SDD1_BG(SDD1_GCD *associatedGCD, uint8_t code) :
|
||||
GCD(associatedGCD), code_num(code)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////
|
||||
|
||||
|
||||
void SDD1_BG::prepareDecomp(void)
|
||||
{
|
||||
|
||||
MPScount = 0;
|
||||
LPSind = 0;
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////
|
||||
|
||||
|
||||
uint8_t SDD1_BG::getBit(bool *endOfRun)
|
||||
{
|
||||
|
||||
uint8_t bit;
|
||||
|
||||
if(!(MPScount || LPSind)) GCD->getRunCount(code_num, &MPScount, &LPSind);
|
||||
|
||||
if(MPScount) {
|
||||
bit = 0;
|
||||
MPScount--;
|
||||
} else {
|
||||
bit = 1;
|
||||
LPSind = 0;
|
||||
}
|
||||
|
||||
if(MPScount || LPSind) (*endOfRun) = 0;
|
||||
else (*endOfRun) = 1;
|
||||
|
||||
return bit;
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
|
||||
SDD1_PEM::SDD1_PEM(SDD1_BG *associatedBG0, SDD1_BG *associatedBG1,
|
||||
SDD1_BG *associatedBG2, SDD1_BG *associatedBG3,
|
||||
SDD1_BG *associatedBG4, SDD1_BG *associatedBG5,
|
||||
SDD1_BG *associatedBG6, SDD1_BG *associatedBG7)
|
||||
{
|
||||
|
||||
BG[0] = associatedBG0;
|
||||
BG[1] = associatedBG1;
|
||||
BG[2] = associatedBG2;
|
||||
BG[3] = associatedBG3;
|
||||
BG[4] = associatedBG4;
|
||||
BG[5] = associatedBG5;
|
||||
BG[6] = associatedBG6;
|
||||
BG[7] = associatedBG7;
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
const SDD1_PEM::state SDD1_PEM::evolution_table[] = {
|
||||
{ 0,25,25},
|
||||
{ 0, 2, 1},
|
||||
{ 0, 3, 1},
|
||||
{ 0, 4, 2},
|
||||
{ 0, 5, 3},
|
||||
{ 1, 6, 4},
|
||||
{ 1, 7, 5},
|
||||
{ 1, 8, 6},
|
||||
{ 1, 9, 7},
|
||||
{ 2,10, 8},
|
||||
{ 2,11, 9},
|
||||
{ 2,12,10},
|
||||
{ 2,13,11},
|
||||
{ 3,14,12},
|
||||
{ 3,15,13},
|
||||
{ 3,16,14},
|
||||
{ 3,17,15},
|
||||
{ 4,18,16},
|
||||
{ 4,19,17},
|
||||
{ 5,20,18},
|
||||
{ 5,21,19},
|
||||
{ 6,22,20},
|
||||
{ 6,23,21},
|
||||
{ 7,24,22},
|
||||
{ 7,24,23},
|
||||
{ 0,26, 1},
|
||||
{ 1,27, 2},
|
||||
{ 2,28, 4},
|
||||
{ 3,29, 8},
|
||||
{ 4,30,12},
|
||||
{ 5,31,16},
|
||||
{ 6,32,18},
|
||||
{ 7,24,22}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void SDD1_PEM::prepareDecomp(void)
|
||||
{
|
||||
|
||||
for(uint8_t i = 0; i < 32; i++) {
|
||||
contextInfo[i].status = 0;
|
||||
contextInfo[i].MPS = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
uint8_t SDD1_PEM::getBit(uint8_t context)
|
||||
{
|
||||
|
||||
bool endOfRun;
|
||||
uint8_t bit;
|
||||
|
||||
SDD1_ContextInfo *pContInfo = &contextInfo[context];
|
||||
uint8_t currStatus = pContInfo->status;
|
||||
const state *pState = &SDD1_PEM::evolution_table[currStatus];
|
||||
uint8_t currentMPS = pContInfo->MPS;
|
||||
|
||||
bit = (BG[pState->code_num])->getBit(&endOfRun);
|
||||
|
||||
if(endOfRun)
|
||||
if(bit) {
|
||||
if(!(currStatus & 0xfe)) (pContInfo->MPS) ^= 0x01;
|
||||
(pContInfo->status) = pState->nextIfLPS;
|
||||
} else
|
||||
(pContInfo->status) = pState->nextIfMPS;
|
||||
|
||||
return bit ^ currentMPS;
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
SDD1_CM::SDD1_CM(SDD1_PEM *associatedPEM) :
|
||||
PEM(associatedPEM)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void SDD1_CM::prepareDecomp(uint8_t firstByte)
|
||||
{
|
||||
bitplanesInfo = firstByte & 0xc0;
|
||||
contextBitsInfo = firstByte & 0x30;
|
||||
bit_number = 0;
|
||||
for(int i = 0; i < 8; i++) prevBitplaneBits[i] = 0;
|
||||
switch(bitplanesInfo) {
|
||||
case 0x00:
|
||||
currBitplane = 1;
|
||||
break;
|
||||
case 0x40:
|
||||
currBitplane = 7;
|
||||
break;
|
||||
case 0x80:
|
||||
currBitplane = 3;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
uint8_t SDD1_CM::getBit(void)
|
||||
{
|
||||
|
||||
uint8_t currContext;
|
||||
uint16_t *context_bits;
|
||||
|
||||
switch(bitplanesInfo) {
|
||||
case 0x00:
|
||||
currBitplane ^= 0x01;
|
||||
break;
|
||||
case 0x40:
|
||||
currBitplane ^= 0x01;
|
||||
if(!(bit_number & 0x7f)) currBitplane = ((currBitplane + 2) & 0x07);
|
||||
break;
|
||||
case 0x80:
|
||||
currBitplane ^= 0x01;
|
||||
if(!(bit_number & 0x7f)) currBitplane ^= 0x02;
|
||||
break;
|
||||
case 0xc0:
|
||||
currBitplane = bit_number & 0x07;
|
||||
}
|
||||
|
||||
context_bits = &prevBitplaneBits[currBitplane];
|
||||
|
||||
currContext = (currBitplane & 0x01) << 4;
|
||||
switch(contextBitsInfo) {
|
||||
case 0x00:
|
||||
currContext |= ((*context_bits & 0x01c0) >> 5) | (*context_bits & 0x0001);
|
||||
break;
|
||||
case 0x10:
|
||||
currContext |= ((*context_bits & 0x0180) >> 5) | (*context_bits & 0x0001);
|
||||
break;
|
||||
case 0x20:
|
||||
currContext |= ((*context_bits & 0x00c0) >> 5) | (*context_bits & 0x0001);
|
||||
break;
|
||||
case 0x30:
|
||||
currContext |= ((*context_bits & 0x0180) >> 5) | (*context_bits & 0x0003);
|
||||
}
|
||||
|
||||
uint8_t bit = PEM->getBit(currContext);
|
||||
|
||||
*context_bits <<= 1;
|
||||
*context_bits |= bit;
|
||||
|
||||
bit_number++;
|
||||
|
||||
return bit;
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
|
||||
SDD1_OL::SDD1_OL(SDD1_CM *associatedCM) :
|
||||
CM(associatedCM)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
|
||||
void SDD1_OL::prepareDecomp(uint8_t firstByte)
|
||||
{
|
||||
bitplanesInfo = firstByte & 0xc0;
|
||||
_regs[0] = 1;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
|
||||
uint8_t SDD1_OL::decompressByte()
|
||||
{
|
||||
switch(bitplanesInfo) {
|
||||
case 0x00:
|
||||
case 0x40:
|
||||
case 0x80:
|
||||
if(_regs[0] == 0) {
|
||||
_regs[0] = ~_regs[0];
|
||||
return _regs[2];
|
||||
} else {
|
||||
_regs[1] = 0;
|
||||
_regs[2] = 0;
|
||||
_regs[0] = 0x80;
|
||||
|
||||
while(_regs[0]) {
|
||||
if(CM->getBit()) {
|
||||
_regs[1] |= _regs[0];
|
||||
}
|
||||
|
||||
if(CM->getBit()) {
|
||||
_regs[2] |= _regs[0];
|
||||
}
|
||||
|
||||
_regs[0] >>= 1;
|
||||
}
|
||||
return _regs[1];
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xC0:
|
||||
_regs[1] = 0;
|
||||
_regs[0] = 0x01;
|
||||
while(_regs[0]) {
|
||||
if(CM->getBit()) {
|
||||
_regs[1] |= _regs[0];
|
||||
}
|
||||
_regs[0] <<= 1;
|
||||
}
|
||||
return _regs[1];
|
||||
}
|
||||
|
||||
throw std::runtime_error("SDD1_OL::decompressByte: Unexpected value");
|
||||
}
|
||||
|
||||
void Sdd1Decomp::Init(Sdd1Mmc *mmc, uint32_t readAddr)
|
||||
{
|
||||
uint8_t firstByte = mmc->ReadRom(readAddr);
|
||||
IM.prepareDecomp(mmc, readAddr);
|
||||
BG0.prepareDecomp();
|
||||
BG1.prepareDecomp();
|
||||
BG2.prepareDecomp();
|
||||
BG3.prepareDecomp();
|
||||
BG4.prepareDecomp();
|
||||
BG5.prepareDecomp();
|
||||
BG6.prepareDecomp();
|
||||
BG7.prepareDecomp();
|
||||
PEM.prepareDecomp();
|
||||
CM.prepareDecomp(firstByte);
|
||||
OL.prepareDecomp(firstByte);
|
||||
}
|
||||
|
||||
uint8_t Sdd1Decomp::GetDecompressedByte()
|
||||
{
|
||||
return OL.decompressByte();
|
||||
}
|
||||
|
||||
Sdd1Decomp::Sdd1Decomp() :
|
||||
GCD(&IM),
|
||||
BG0(&GCD, 0), BG1(&GCD, 1), BG2(&GCD, 2), BG3(&GCD, 3),
|
||||
BG4(&GCD, 4), BG5(&GCD, 5), BG6(&GCD, 6), BG7(&GCD, 7),
|
||||
PEM(&BG0, &BG1, &BG2, &BG3, &BG4, &BG5, &BG6, &BG7),
|
||||
CM(&PEM),
|
||||
OL(&CM)
|
||||
{
|
||||
|
||||
}
|
170
Core/Sdd1Decomp.h
Normal file
170
Core/Sdd1Decomp.h
Normal file
|
@ -0,0 +1,170 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
|
||||
/************************************************************************
|
||||
|
||||
S-DD1'algorithm emulation code
|
||||
------------------------------
|
||||
|
||||
Author: Andreas Naive
|
||||
Date: August 2003
|
||||
Last update: October 2004
|
||||
|
||||
This code is Public Domain. There is no copyright holded by the author.
|
||||
Said this, the author wish to explicitly emphasize his inalienable moral rights
|
||||
over this piece of intelectual work and the previous research that made it
|
||||
possible, as recognized by most of the copyright laws around the world.
|
||||
|
||||
This code is provided 'as-is', with no warranty, expressed or implied.
|
||||
No responsability is assumed by the author in connection with it.
|
||||
|
||||
The author is greatly indebted with The Dumper, without whose help and
|
||||
patience providing him with real S-DD1 data the research would have never been
|
||||
possible. He also wish to note that in the very beggining of his research,
|
||||
Neviksti had done some steps in the right direction. By last, the author is
|
||||
indirectly indebted to all the people that worked and contributed in the
|
||||
S-DD1 issue in the past.
|
||||
|
||||
An algorithm's documentation is available as a separate document.
|
||||
The implementation is obvious when the algorithm is
|
||||
understood.
|
||||
|
||||
************************************************************************/
|
||||
|
||||
class Sdd1Mmc;
|
||||
|
||||
class SDD1_IM
|
||||
{ //Input Manager
|
||||
Sdd1Mmc* _sdd1Mmc;
|
||||
public:
|
||||
SDD1_IM(void) {}
|
||||
void prepareDecomp(Sdd1Mmc *mmc, uint32_t readAddr);
|
||||
uint8_t getCodeword(const uint8_t code_len);
|
||||
|
||||
private:
|
||||
uint32_t _readAddr;
|
||||
uint8_t bit_count;
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class SDD1_GCD
|
||||
{ //Golomb-Code Decoder
|
||||
|
||||
public:
|
||||
SDD1_GCD(SDD1_IM *associatedIM);
|
||||
void getRunCount(uint8_t code_num, uint8_t *MPScount, bool *LPSind);
|
||||
|
||||
private:
|
||||
SDD1_IM * const IM;
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class SDD1_BG
|
||||
{ // Bits Generator
|
||||
|
||||
public:
|
||||
SDD1_BG(SDD1_GCD *associatedGCD, uint8_t code);
|
||||
void prepareDecomp(void);
|
||||
uint8_t getBit(bool *endOfRun);
|
||||
|
||||
private:
|
||||
const uint8_t code_num;
|
||||
uint8_t MPScount;
|
||||
bool LPSind;
|
||||
SDD1_GCD *const GCD;
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////
|
||||
|
||||
|
||||
class SDD1_PEM
|
||||
{ //Probability Estimation Module
|
||||
|
||||
public:
|
||||
SDD1_PEM(SDD1_BG *associatedBG0, SDD1_BG *associatedBG1,
|
||||
SDD1_BG *associatedBG2, SDD1_BG *associatedBG3,
|
||||
SDD1_BG *associatedBG4, SDD1_BG *associatedBG5,
|
||||
SDD1_BG *associatedBG6, SDD1_BG *associatedBG7);
|
||||
void prepareDecomp(void);
|
||||
uint8_t getBit(uint8_t context);
|
||||
|
||||
private:
|
||||
struct state
|
||||
{
|
||||
uint8_t code_num;
|
||||
uint8_t nextIfMPS;
|
||||
uint8_t nextIfLPS;
|
||||
};
|
||||
static const state evolution_table[];
|
||||
struct SDD1_ContextInfo
|
||||
{
|
||||
uint8_t status;
|
||||
uint8_t MPS;
|
||||
} contextInfo[32];
|
||||
SDD1_BG * BG[8];
|
||||
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
|
||||
class SDD1_CM
|
||||
{ //Context Model
|
||||
|
||||
public:
|
||||
SDD1_CM(SDD1_PEM *associatedPEM);
|
||||
void prepareDecomp(uint8_t firstByte);
|
||||
uint8_t getBit(void);
|
||||
|
||||
private:
|
||||
uint8_t bitplanesInfo;
|
||||
uint8_t contextBitsInfo;
|
||||
uint8_t bit_number;
|
||||
uint8_t currBitplane;
|
||||
uint16_t prevBitplaneBits[8];
|
||||
SDD1_PEM *const PEM;
|
||||
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
|
||||
class SDD1_OL
|
||||
{ //Output Logic
|
||||
|
||||
public:
|
||||
SDD1_OL(SDD1_CM *associatedCM);
|
||||
void prepareDecomp(uint8_t firstByte);
|
||||
uint8_t decompressByte();
|
||||
|
||||
private:
|
||||
uint8_t bitplanesInfo;
|
||||
uint8_t _regs[3];
|
||||
SDD1_CM *const CM;
|
||||
|
||||
};
|
||||
|
||||
class Sdd1Decomp
|
||||
{
|
||||
public:
|
||||
Sdd1Decomp();
|
||||
void Init(Sdd1Mmc *mmc, uint32_t readAddr);
|
||||
uint8_t GetDecompressedByte();
|
||||
|
||||
private:
|
||||
SDD1_IM IM;
|
||||
SDD1_GCD GCD;
|
||||
SDD1_BG BG0; SDD1_BG BG1; SDD1_BG BG2; SDD1_BG BG3;
|
||||
SDD1_BG BG4; SDD1_BG BG5; SDD1_BG BG6; SDD1_BG BG7;
|
||||
SDD1_PEM PEM;
|
||||
SDD1_CM CM;
|
||||
SDD1_OL OL;
|
||||
|
||||
};
|
84
Core/Sdd1Mmc.cpp
Normal file
84
Core/Sdd1Mmc.cpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
#include "stdafx.h"
|
||||
#include "Sdd1Mmc.h"
|
||||
#include "Sdd1Types.h"
|
||||
#include "BaseCartridge.h"
|
||||
|
||||
Sdd1Mmc::Sdd1Mmc(Sdd1State &state, BaseCartridge *cart)
|
||||
{
|
||||
_romHandlers = &cart->GetPrgRomHandlers();
|
||||
_handlerMask = (uint32_t)((*_romHandlers).size() - 1);
|
||||
_state = &state;
|
||||
}
|
||||
|
||||
uint8_t Sdd1Mmc::ReadRom(uint32_t addr)
|
||||
{
|
||||
return GetHandler(addr)->Read(addr);
|
||||
}
|
||||
|
||||
IMemoryHandler* Sdd1Mmc::GetHandler(uint32_t addr)
|
||||
{
|
||||
uint8_t bank = (addr >> 20) - 0x0C;
|
||||
uint16_t handlerIndex = ((_state->SelectedBanks[bank] & 0x0F) << 8) | ((addr & 0xFF000) >> 12);
|
||||
|
||||
return (*_romHandlers)[handlerIndex & _handlerMask].get();
|
||||
}
|
||||
|
||||
uint8_t Sdd1Mmc::Read(uint32_t addr)
|
||||
{
|
||||
if(!(addr & 0x400000)) {
|
||||
uint16_t handlerIndex;
|
||||
if(((addr & 0x800000) && _state->SelectedBanks[3] & 0x80) || (!(addr & 0x800000) && (_state->SelectedBanks[1] & 0x80))) {
|
||||
//Force mirroring: $20-$3F mirrors $00-$1F, $A0-$BF mirrors $80-$9F
|
||||
handlerIndex = (((addr & 0x1F0000) >> 1) | (addr & 0x7000)) >> 12;
|
||||
} else {
|
||||
handlerIndex = (((addr & 0x3F0000) >> 1) | (addr & 0x7000)) >> 12;
|
||||
}
|
||||
return (*_romHandlers)[handlerIndex & _handlerMask]->Read(addr);
|
||||
}
|
||||
|
||||
//Banks $C0+
|
||||
uint8_t activeChannels = _state->ProcessNextDma & _state->AllowDmaProcessing;
|
||||
if(activeChannels) {
|
||||
//Some DMA channels are being processed, need to check if the address being read matches one of the dma channels
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if((activeChannels & (1 << i)) && addr == _state->DmaAddress[i]) {
|
||||
if(_state->NeedInit) {
|
||||
_decompressor.Init(this, addr);
|
||||
_state->NeedInit = false;
|
||||
}
|
||||
|
||||
uint8_t data = _decompressor.GetDecompressedByte();
|
||||
|
||||
_state->DmaLength[i]--;
|
||||
if(_state->DmaLength[i] == 0) {
|
||||
_state->NeedInit = true;
|
||||
_state->ProcessNextDma &= ~(1 << i);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ReadRom(addr);
|
||||
}
|
||||
|
||||
uint8_t Sdd1Mmc::Peek(uint32_t addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Sdd1Mmc::PeekBlock(uint8_t *output)
|
||||
{
|
||||
memset(output, 0, 0x1000);
|
||||
}
|
||||
|
||||
void Sdd1Mmc::Write(uint32_t addr, uint8_t value)
|
||||
{
|
||||
//ROM, read-only
|
||||
}
|
||||
|
||||
AddressInfo Sdd1Mmc::GetAbsoluteAddress(uint32_t address)
|
||||
{
|
||||
return GetHandler(address)->GetAbsoluteAddress(address);
|
||||
}
|
30
Core/Sdd1Mmc.h
Normal file
30
Core/Sdd1Mmc.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "IMemoryHandler.h"
|
||||
#include "Sdd1Types.h"
|
||||
#include "Sdd1Decomp.h"
|
||||
|
||||
class BaseCartridge;
|
||||
|
||||
class Sdd1Mmc : public IMemoryHandler
|
||||
{
|
||||
private:
|
||||
Sdd1State* _state;
|
||||
vector<unique_ptr<IMemoryHandler>> *_romHandlers;
|
||||
uint32_t _handlerMask;
|
||||
Sdd1Decomp _decompressor;
|
||||
|
||||
IMemoryHandler* GetHandler(uint32_t addr);
|
||||
|
||||
public:
|
||||
Sdd1Mmc(Sdd1State &state, BaseCartridge *cart);
|
||||
|
||||
uint8_t ReadRom(uint32_t addr);
|
||||
|
||||
// Inherited via IMemoryHandler
|
||||
virtual uint8_t Read(uint32_t addr) override;
|
||||
virtual uint8_t Peek(uint32_t addr) override;
|
||||
virtual void PeekBlock(uint8_t * output) override;
|
||||
virtual void Write(uint32_t addr, uint8_t value) override;
|
||||
virtual AddressInfo GetAbsoluteAddress(uint32_t address) override;
|
||||
};
|
13
Core/Sdd1Types.h
Normal file
13
Core/Sdd1Types.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
|
||||
struct Sdd1State
|
||||
{
|
||||
uint8_t AllowDmaProcessing;
|
||||
uint8_t ProcessNextDma;
|
||||
uint8_t SelectedBanks[4];
|
||||
|
||||
uint32_t DmaAddress[8];
|
||||
uint16_t DmaLength[8];
|
||||
bool NeedInit;
|
||||
};
|
|
@ -169,7 +169,7 @@ namespace Mesen.GUI
|
|||
GSU,
|
||||
OBC1,
|
||||
SA1,
|
||||
DD1,
|
||||
SDD1,
|
||||
RTC,
|
||||
Satellaview,
|
||||
SPC7110,
|
||||
|
|
Loading…
Add table
Reference in a new issue