Skip to content

Commit

Permalink
SNES: Add support for Sufami Turbo games
Browse files Browse the repository at this point in the history
  • Loading branch information
SourMesen committed Jan 3, 2025
1 parent 8d6830a commit 18269da
Show file tree
Hide file tree
Showing 26 changed files with 333 additions and 37 deletions.
1 change: 1 addition & 0 deletions Core/Core.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
<ClInclude Include="SNES\Coprocessors\ST018\ArmV3Types.h" />
<ClInclude Include="SNES\Coprocessors\ST018\St018.h" />
<ClInclude Include="SNES\Coprocessors\ST018\St018Types.h" />
<ClInclude Include="SNES\Coprocessors\SufamiTurbo\SufamiTurbo.h" />
<ClInclude Include="SNES\Debugger\DummyArmV3Cpu.h" />
<ClInclude Include="SNES\Debugger\St018Debugger.h" />
<ClInclude Include="SNES\Debugger\St018DisUtils.h" />
Expand Down
6 changes: 6 additions & 0 deletions Core/Core.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -2970,6 +2970,9 @@
<ClInclude Include="Shared\ArmEnums.h">
<Filter>Shared</Filter>
</ClInclude>
<ClInclude Include="SNES\Coprocessors\SufamiTurbo\SufamiTurbo.h">
<Filter>SNES\Coprocessors\SufamiTurbo</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Shared\Video\RotateFilter.cpp">
Expand Down Expand Up @@ -3457,5 +3460,8 @@
<Filter Include="SNES\Coprocessors\ST018">
<UniqueIdentifier>{387c0b44-8063-45e8-a28a-b6d9e27b5a3f}</UniqueIdentifier>
</Filter>
<Filter Include="SNES\Coprocessors\SufamiTurbo">
<UniqueIdentifier>{bfdd4bb9-41c2-4255-a8c3-0176c990cbf3}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
6 changes: 6 additions & 0 deletions Core/Debugger/DebugUtilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ class DebugUtilities
case MemoryType::SnesWorkRam:
case MemoryType::BsxMemoryPack:
case MemoryType::BsxPsRam:
case MemoryType::SufamiTurboFirmware:
case MemoryType::SufamiTurboSecondCart:
case MemoryType::SufamiTurboSecondCartRam:
case MemoryType::SnesRegister:
return CpuType::Snes;

Expand Down Expand Up @@ -232,6 +235,8 @@ class DebugUtilities
case MemoryType::DspProgramRom:
case MemoryType::St018PrgRom:
case MemoryType::St018DataRom:
case MemoryType::SufamiTurboFirmware:
case MemoryType::SufamiTurboSecondCart:
case MemoryType::SpcRom:
case MemoryType::SmsPrgRom:
case MemoryType::SmsBootRom:
Expand All @@ -255,6 +260,7 @@ class DebugUtilities
case MemoryType::NesSaveRam:
case MemoryType::GbCartRam:
case MemoryType::SnesSaveRam:
case MemoryType::SufamiTurboSecondCartRam:
case MemoryType::PceSaveRam:
case MemoryType::SnesRegister:
case MemoryType::SmsCartRam:
Expand Down
63 changes: 51 additions & 12 deletions Core/SNES/BaseCartridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "SNES/Coprocessors/BSX/BsxCart.h"
#include "SNES/Coprocessors/BSX/BsxMemoryPack.h"
#include "SNES/Coprocessors/SGB/SuperGameboy.h"
#include "SNES/Coprocessors/SufamiTurbo/SufamiTurbo.h"
#include "Shared/EmuSettings.h"
#include "Shared/SettingTypes.h"
#include "Shared/BatteryManager.h"
Expand Down Expand Up @@ -64,12 +65,18 @@ unique_ptr<BaseCartridge> BaseCartridge::CreateCartridge(SnesConsole* console, V
}
cart->LoadRom();
cart->_emu->RegisterMemory(MemoryType::SnesPrgRom, cart->_prgRom, cart->_prgRomSize);
} else if(fileExt == ".st") {
if(cart->LoadSufamiTurbo(romFile)) {
return cart;
} else {
return nullptr;
}
} else if(fileExt == ".gb" || fileExt == ".gbc" || fileExt == ".gbx") {
if(cart->LoadGameboy(romFile)) {
return cart;
} else {
return nullptr;
}
}
} else {
if(romData.size() < 0x8000) {
return nullptr;
Expand All @@ -88,7 +95,7 @@ unique_ptr<BaseCartridge> BaseCartridge::CreateCartridge(SnesConsole* console, V
}
} else {
cart->LoadRom();
cart->EnsureValidPrgRomSize();
BaseCartridge::EnsureValidPrgRomSize(cart->_prgRomSize, cart->_prgRom);
}

cart->_emu->RegisterMemory(MemoryType::SnesPrgRom, cart->_prgRom, cart->_prgRomSize);
Expand All @@ -100,18 +107,18 @@ unique_ptr<BaseCartridge> BaseCartridge::CreateCartridge(SnesConsole* console, V
}
}

void BaseCartridge::EnsureValidPrgRomSize()
void BaseCartridge::EnsureValidPrgRomSize(uint32_t& size, uint8_t*& rom)
{
if((_prgRomSize & 0xFFF) != 0) {
if((size & 0xFFF) != 0) {
//Round up to the next 4kb size, to ensure we have access to all the rom's data
//Memory mappings expect a multiple of 4kb to work properly
uint32_t orgPrgSize = _prgRomSize;
_prgRomSize = (_prgRomSize & ~0xFFF) + 0x1000;
uint8_t* expandedPrgRom = new uint8_t[_prgRomSize];
memset(expandedPrgRom, 0, _prgRomSize);
memcpy(expandedPrgRom, _prgRom, orgPrgSize);
delete[] _prgRom;
_prgRom = expandedPrgRom;
uint32_t orgPrgSize = size;
size = (size & ~0xFFF) + 0x1000;
uint8_t* expandedPrgRom = new uint8_t[size];
memset(expandedPrgRom, 0, size);
memcpy(expandedPrgRom, rom, orgPrgSize);
delete[] rom;
rom = expandedPrgRom;
}
}

Expand Down Expand Up @@ -404,6 +411,10 @@ void BaseCartridge::SaveBattery()
if(_gameboy) {
_gameboy->SaveBattery();
}

if(_sufamiTurbo) {
_sufamiTurbo->SaveBattery();
}
}

void BaseCartridge::Init(MemoryMappings &mm)
Expand Down Expand Up @@ -555,7 +566,11 @@ bool BaseCartridge::MapSpecificCarts(MemoryMappings &mm)
{
string name = GetCartName();
string code = GetGameCode();
if(GetCartName() == "DEZAEMON") {

if(_sufamiTurbo) {
_sufamiTurbo->InitializeMappings(mm, _prgRomHandlers, _saveRamHandlers);
return true;
} else if(GetCartName() == "DEZAEMON") {
//LOROM with mirrored SRAM?
mm.RegisterHandler(0x00, 0x7D, 0x8000, 0xFFFF, _prgRomHandlers);
mm.RegisterHandler(0x80, 0xFF, 0x8000, 0xFFFF, _prgRomHandlers);
Expand Down Expand Up @@ -644,6 +659,30 @@ void BaseCartridge::LoadSpc()
SetupCpuHalt();
}

bool BaseCartridge::LoadSufamiTurbo(VirtualFile& romFile)
{
_sufamiTurbo.reset(SufamiTurbo::Init(_emu, romFile));
if(!_sufamiTurbo) {
return false;
}

vector<uint8_t> romData;
romFile.ReadFile(romData);

_prgRomSize = (uint32_t)romData.size();
_prgRom = new uint8_t[_prgRomSize];
memcpy(_prgRom, romData.data(), romData.size());
BaseCartridge::EnsureValidPrgRomSize(_prgRomSize, _prgRom);
_emu->RegisterMemory(MemoryType::SnesPrgRom, _prgRom, _prgRomSize);

_saveRamSize = SufamiTurbo::GetSaveRamSize(romData);
_saveRam = new uint8_t[_saveRamSize];
_emu->RegisterMemory(MemoryType::SnesSaveRam, _saveRam, _saveRamSize);
_emu->GetSettings()->InitializeRam(GetRamPowerOnState(), _saveRam, _saveRamSize);

return true;
}

bool BaseCartridge::LoadGameboy(VirtualFile& romFile)
{
_cartInfo = { };
Expand Down
9 changes: 8 additions & 1 deletion Core/SNES/BaseCartridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class Gameboy;
class SnesConsole;
class Emulator;
class SpcFileData;
class SufamiTurbo;
enum class ConsoleRegion;
enum class RamState;

Expand All @@ -47,6 +48,7 @@ class BaseCartridge : public ISerializable
BsxCart* _bsx = nullptr;
unique_ptr<BsxMemoryPack> _bsxMemPack;
unique_ptr<Gameboy> _gameboy;
unique_ptr<SufamiTurbo> _sufamiTurbo;

CartFlags::CartFlags _flags = CartFlags::CartFlags::None;
CoprocessorType _coprocessorType = CoprocessorType::None;
Expand Down Expand Up @@ -83,10 +85,13 @@ class BaseCartridge : public ISerializable
void InitRamPowerOnState();

void LoadRom();
void EnsureValidPrgRomSize();

void LoadSpc();

bool LoadSufamiTurbo(VirtualFile& romFile);

bool LoadGameboy(VirtualFile& romFile);

void SetupCpuHalt();
void InitCoprocessor();
void LoadEmbeddedFirmware();
Expand All @@ -99,6 +104,8 @@ class BaseCartridge : public ISerializable

static unique_ptr<BaseCartridge> CreateCartridge(SnesConsole* console, VirtualFile &romFile);

static void EnsureValidPrgRomSize(uint32_t& size, uint8_t*& rom);

void Reset();

void SaveBattery();
Expand Down
148 changes: 148 additions & 0 deletions Core/SNES/Coprocessors/SufamiTurbo/SufamiTurbo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#pragma once
#include "pch.h"
#include "SNES/IMemoryHandler.h"
#include "SNES/RomHandler.h"
#include "SNES/MemoryMappings.h"
#include "Shared/Emulator.h"
#include "Shared/BatteryManager.h"
#include "Shared/FirmwareHelper.h"
#include "Utilities/VirtualFile.h"

struct SufamiTurboFilePromptMessage
{
char Filename[5000];
};

class SufamiTurbo
{
private:
Emulator* _emu = nullptr;
string _nameSlotA;

uint8_t* _firmware = nullptr;
uint32_t _firmwareSize = 0;

string _cartName;
uint8_t* _cartRom = nullptr;
uint32_t _cartRomSize = 0;

uint8_t* _cartRam = nullptr;
uint32_t _cartRamSize = 0;

vector<unique_ptr<IMemoryHandler>> _firmwareHandlers;
vector<unique_ptr<IMemoryHandler>> _cartRomHandlers;
vector<unique_ptr<IMemoryHandler>> _cartRamHandlers;

SufamiTurbo() {}

public:
static SufamiTurbo* Init(Emulator* emu, VirtualFile& slotA)
{
vector<uint8_t> firmware;
if(!FirmwareHelper::LoadSufamiTurboFirmware(emu, firmware)) {
return nullptr;
}

SufamiTurbo* st = new SufamiTurbo();
st->_emu = emu;
st->_nameSlotA = FolderUtilities::GetFilename(slotA.GetFileName(), false);

st->_firmwareSize = (uint32_t)firmware.size();
st->_firmware = new uint8_t[st->_firmwareSize];
memcpy(st->_firmware, firmware.data(), firmware.size());
BaseCartridge::EnsureValidPrgRomSize(st->_firmwareSize, st->_firmware);
emu->RegisterMemory(MemoryType::SufamiTurboFirmware, st->_firmware, st->_firmwareSize);
for(uint32_t i = 0; i < st->_firmwareSize; i += 0x1000) {
st->_firmwareHandlers.push_back(unique_ptr<RomHandler>(new RomHandler(st->_firmware, i, st->_firmwareSize, MemoryType::SufamiTurboFirmware)));
}

SufamiTurboFilePromptMessage msg = {};
emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::SufamiTurboFilePrompt, &msg);

string slot2File = string(msg.Filename, strlen(msg.Filename));
if(slot2File.size()) {
VirtualFile file = slot2File;
if(file.IsValid()) {
vector<uint8_t> cart;
file.ReadFile(cart);

st->_cartName = FolderUtilities::GetFilename(file.GetFileName(), false);
if(st->_nameSlotA == st->_cartName) {
st->_cartName += "_SlotB";
}

st->_cartRomSize = (uint32_t)cart.size();
st->_cartRom = new uint8_t[st->_cartRomSize];
memcpy(st->_cartRom, cart.data(), cart.size());
BaseCartridge::EnsureValidPrgRomSize(st->_cartRomSize, st->_cartRom);

emu->RegisterMemory(MemoryType::SufamiTurboSecondCart, st->_cartRom, st->_cartRomSize);

for(uint32_t i = 0; i < st->_cartRomSize; i += 0x1000) {
st->_cartRomHandlers.push_back(unique_ptr<RomHandler>(new RomHandler(st->_cartRom, i, st->_cartRomSize, MemoryType::SufamiTurboSecondCart)));
}

st->_cartRamSize = GetSaveRamSize(cart);
st->_cartRam = new uint8_t[st->_cartRamSize];
emu->RegisterMemory(MemoryType::SufamiTurboSecondCartRam, st->_cartRam, st->_cartRamSize);
memset(st->_cartRam, 0, st->_cartRamSize);

emu->GetBatteryManager()->LoadBattery(st->_cartName + ".srm", st->_cartRam, st->_cartRamSize);

for(uint32_t i = 0; i < st->_cartRamSize; i += 0x1000) {
st->_cartRamHandlers.push_back(unique_ptr<RamHandler>(new RamHandler(st->_cartRam, i, st->_cartRamSize, MemoryType::SufamiTurboSecondCartRam)));
}
}
}

return st;
}

static uint32_t GetSaveRamSize(vector<uint8_t>& cart)
{
auto checkMarker = [&](string marker) {
return std::search((char*)cart.data(), (char*)cart.data()+cart.size(), marker.c_str(), marker.c_str()+marker.size()) != (char*)cart.data() + cart.size();
};

if(checkMarker("POIPOI.Ver") || checkMarker("SDBATTLE ")) {
return 0x800;
} else if(checkMarker("SD \xB6\xDE\xDD\xC0\xDE\xD1 GN")) {
//SD ガンダム GN
return 0x2000;
}

return 0;
}

void InitializeMappings(MemoryMappings& mm, vector<unique_ptr<IMemoryHandler>>& prgRomHandlers, vector<unique_ptr<IMemoryHandler>>& saveRamHandlers)
{
mm.RegisterHandler(0x20, 0x3F, 0x8000, 0xFFFF, prgRomHandlers);
mm.RegisterHandler(0xA0, 0xBF, 0x8000, 0xFFFF, prgRomHandlers);

mm.RegisterHandler(0x60, 0x63, 0x8000, 0xFFFF, saveRamHandlers);
mm.RegisterHandler(0xE0, 0xE3, 0x8000, 0xFFFF, saveRamHandlers);

mm.RegisterHandler(0x00, 0x1F, 0x8000, 0xFFFF, _firmwareHandlers);
mm.RegisterHandler(0x80, 0x9F, 0x8000, 0xFFFF, _firmwareHandlers);

mm.RegisterHandler(0x40, 0x5F, 0x8000, 0xFFFF, _cartRomHandlers);
mm.RegisterHandler(0xC0, 0xDF, 0x8000, 0xFFFF, _cartRomHandlers);

mm.RegisterHandler(0x70, 0x73, 0x8000, 0xFFFF, _cartRamHandlers);
mm.RegisterHandler(0xF0, 0xF3, 0x8000, 0xFFFF, _cartRamHandlers);
}

void SaveBattery()
{
if(_cartRam) {
_emu->GetBatteryManager()->SaveBattery(_cartName + ".srm", _cartRam, _cartRamSize);
}
}

~SufamiTurbo()
{
delete[] _firmware;
delete[] _cartRom;
delete[] _cartRam;
}
};
1 change: 1 addition & 0 deletions Core/SNES/SnesConsole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ AddressInfo SnesConsole::GetRelativeAddress(AddressInfo& absAddress, CpuType cpu
case MemoryType::SnesPrgRom:
case MemoryType::SnesWorkRam:
case MemoryType::SnesSaveRam:
case MemoryType::SufamiTurboFirmware:
{
if(!mappings) {
return unmapped;
Expand Down
2 changes: 1 addition & 1 deletion Core/SNES/SnesConsole.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class SnesConsole final : public IConsole
SnesConsole(Emulator* emu);
~SnesConsole();

static vector<string> GetSupportedExtensions() { return { ".sfc", ".swc", ".fig", ".smc", ".bs", ".gb", ".gbc", ".gbx", ".spc" }; }
static vector<string> GetSupportedExtensions() { return { ".sfc", ".swc", ".fig", ".smc", ".bs", ".gb", ".gbc", ".gbx", ".spc", ".st" }; }
static vector<string> GetSupportedSignatures() { return { "SNES-SPC700 Sound File Data" }; }

void Initialize();
Expand Down
Loading

0 comments on commit 18269da

Please sign in to comment.