mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
359 lines
9 KiB
C++
359 lines
9 KiB
C++
#include "pch.h"
|
|
#include "SMS/SmsConsole.h"
|
|
#include "SMS/SmsCpu.h"
|
|
#include "SMS/SmsControlManager.h"
|
|
#include "SMS/SmsVdp.h"
|
|
#include "SMS/SmsPsg.h"
|
|
#include "SMS/SmsFmAudio.h"
|
|
#include "SMS/SmsMemoryManager.h"
|
|
#include "SMS/SmsDefaultVideoFilter.h"
|
|
#include "SMS/SmsNtscFilter.h"
|
|
#include "SMS/SmsTypes.h"
|
|
#include "SMS/Carts/SmsSegaCart.h"
|
|
#include "SMS/Carts/SmsCodemasterCart.h"
|
|
#include "SMS/Carts/SmsMsxCart.h"
|
|
#include "SMS/Carts/SmsKoreanCart.h"
|
|
#include "SMS/Carts/SmsNemesisCart.h"
|
|
#include "Shared/Emulator.h"
|
|
#include "Shared/CheatManager.h"
|
|
#include "Shared/FirmwareHelper.h"
|
|
#include "Utilities/Serializer.h"
|
|
#include "Utilities/StringUtilities.h"
|
|
#include "Utilities/CRC32.h"
|
|
|
|
SmsConsole::SmsConsole(Emulator* emu)
|
|
{
|
|
_emu = emu;
|
|
}
|
|
|
|
SmsConsole::~SmsConsole()
|
|
{
|
|
}
|
|
|
|
LoadRomResult SmsConsole::LoadRom(VirtualFile& romFile)
|
|
{
|
|
vector<uint8_t> romData;
|
|
romFile.ReadFile(romData);
|
|
|
|
if(romData.size() >= 0x100) {
|
|
if((romData.size() % 0x400) == 0x200) {
|
|
//ROM has 512-byte copier header, ignore it
|
|
romData.erase(romData.begin(), romData.begin() + 0x200);
|
|
}
|
|
|
|
_filename = romFile.GetFileName();
|
|
|
|
string ext = romFile.GetFileExtension();
|
|
bool isGameGear = ext == ".gg";
|
|
if(isGameGear) {
|
|
_romFormat = RomFormat::GameGear;
|
|
_model = SmsModel::GameGear;
|
|
} else if(ext == ".col") {
|
|
_romFormat = RomFormat::ColecoVision;
|
|
_model = SmsModel::ColecoVision;
|
|
} else if(ext == ".sg") {
|
|
_romFormat = RomFormat::Sg;
|
|
_model = SmsModel::Sg;
|
|
} else {
|
|
_romFormat = RomFormat::Sms;
|
|
_model = SmsModel::Sms;
|
|
}
|
|
|
|
_vdp.reset(new SmsVdp());
|
|
_memoryManager.reset(new SmsMemoryManager());
|
|
_cpu.reset(new SmsCpu());
|
|
_psg.reset(new SmsPsg(_emu, this));
|
|
_fmAudio.reset(new SmsFmAudio(_emu, this));
|
|
_controlManager.reset(new SmsControlManager(_emu, this, _vdp.get()));
|
|
|
|
InitCart(romData);
|
|
|
|
vector<uint8_t> biosRom;
|
|
if(_model == SmsModel::ColecoVision) {
|
|
FirmwareHelper::LoadColecoVisionBios(_emu, biosRom);
|
|
} else {
|
|
FirmwareHelper::LoadSmsBios(_emu, biosRom, isGameGear);
|
|
}
|
|
|
|
_memoryManager->Init(_emu, this, romData, biosRom, _vdp.get(), _controlManager.get(), _cart.get(), _psg.get(), _fmAudio.get());
|
|
_vdp->Init(_emu, this, _cpu.get(), _controlManager.get(), _memoryManager.get());
|
|
_cpu->Init(_emu, this, _memoryManager.get());
|
|
|
|
UpdateRegion(true);
|
|
|
|
return LoadRomResult::Success;
|
|
}
|
|
|
|
return LoadRomResult::Failure;
|
|
}
|
|
|
|
void SmsConsole::InitCart(vector<uint8_t>& romData)
|
|
{
|
|
bool isCodemasterMapper = false;
|
|
if(romData.size() > 0x8000) {
|
|
//Codemaster games have a checksum+complement at 7FE6-7FE9
|
|
uint32_t checksum = romData[0x7FE6] | (romData[0x7FE7] << 8);
|
|
uint32_t complement = romData[0x7FE8] | (romData[0x7FE9] << 8);
|
|
isCodemasterMapper = (complement + checksum) == 0x10000;
|
|
}
|
|
|
|
if(isCodemasterMapper) {
|
|
_cart.reset(new SmsCodemasterCart(_memoryManager.get()));
|
|
} else {
|
|
uint32_t crc = CRC32::GetCRC(romData);
|
|
switch(crc) {
|
|
case 0x77EFE84A: //Cyborg Z
|
|
case 0x06965ED9: //F-1 Spirit - The way to Formula-1
|
|
case 0xF89AF3CC: //Knightmare II: The Maze of Galious
|
|
case 0x0A77FA5E: //Nemesis 2
|
|
case 0x445525E2: //Penguin Adventure
|
|
case 0x83F0EEDE: //Street Master
|
|
case 0x9195C34C: //Super Boy 3
|
|
case 0xA05258F5: //Wonsiin
|
|
_cart.reset(new SmsMsxCart(_memoryManager.get()));
|
|
break;
|
|
|
|
case 0xE316C06D: //Nemesis
|
|
_cart.reset(new SmsNemesisCart(_memoryManager.get(), (uint32_t)romData.size()));
|
|
break;
|
|
|
|
case 0x89B79E77: //Dallyeora Pigu-Wang
|
|
case 0x97D03541: //Samgukji 3
|
|
case 0x929222C4: //Jang Pung II
|
|
case 0x18FB98A3: //Jang Pung 3
|
|
_cart.reset(new SmsKoreanCart(_memoryManager.get()));
|
|
break;
|
|
|
|
//TODOSMS
|
|
//case 0x192949D5: //Janggun-ui Adeul
|
|
|
|
default:
|
|
if(romData.size() <= 0xC000) {
|
|
//No mapper (up to 48kb of prg rom)
|
|
_cart.reset(new SmsCart(_memoryManager.get()));
|
|
} else {
|
|
_cart.reset(new SmsSegaCart(_memoryManager.get()));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SmsConsole::Reset()
|
|
{
|
|
//SMS/GG has no physical reset button, behave like power cycle
|
|
_emu->ReloadRom(true);
|
|
}
|
|
|
|
void SmsConsole::RunFrame()
|
|
{
|
|
UpdateRegion(false);
|
|
|
|
uint32_t frame = _vdp->GetFrameCount();
|
|
while(frame == _vdp->GetFrameCount()) {
|
|
_cpu->Exec();
|
|
}
|
|
|
|
_psg->Run();
|
|
_psg->PlayQueuedAudio();
|
|
}
|
|
|
|
void SmsConsole::ProcessEndOfFrame()
|
|
{
|
|
_controlManager->UpdateControlDevices();
|
|
_controlManager->UpdateInputState();
|
|
}
|
|
|
|
void SmsConsole::UpdateRegion(bool forceUpdate)
|
|
{
|
|
ConsoleRegion region;
|
|
switch(_model) {
|
|
default:
|
|
case SmsModel::Sms: region = _emu->GetSettings()->GetSmsConfig().Region; break;
|
|
case SmsModel::GameGear: region = _emu->GetSettings()->GetSmsConfig().GameGearRegion; break;
|
|
case SmsModel::ColecoVision: region = _emu->GetSettings()->GetCvConfig().Region; break;
|
|
}
|
|
|
|
if(region == ConsoleRegion::Auto) {
|
|
string filename = StringUtilities::ToLower(_filename);
|
|
if(filename.find("(europe)") != string::npos || filename.find("(e)") != string::npos) {
|
|
region = ConsoleRegion::Pal;
|
|
} else {
|
|
if(_model == SmsModel::GameGear && (filename.find("(japan)") != string::npos || filename.find("(j)") != string::npos)) {
|
|
region = ConsoleRegion::NtscJapan;
|
|
} else {
|
|
region = ConsoleRegion::Ntsc;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(_region != region || forceUpdate) {
|
|
_region = region;
|
|
_vdp->SetRegion(_model == SmsModel::GameGear ? ConsoleRegion::Ntsc : _region);
|
|
_psg->SetRegion(_model == SmsModel::GameGear ? ConsoleRegion::Ntsc : _region);
|
|
}
|
|
}
|
|
|
|
void SmsConsole::SaveBattery()
|
|
{
|
|
_memoryManager->SaveBattery();
|
|
}
|
|
|
|
bool SmsConsole::IsPsgAudioMuted()
|
|
{
|
|
return _fmAudio->IsPsgAudioMuted();
|
|
}
|
|
|
|
bool SmsConsole::HasBios()
|
|
{
|
|
return _memoryManager->HasBios();
|
|
}
|
|
|
|
BaseControlManager* SmsConsole::GetControlManager()
|
|
{
|
|
return _controlManager.get();
|
|
}
|
|
|
|
SmsRevision SmsConsole::GetRevision()
|
|
{
|
|
if(_model == SmsModel::Sg || _model == SmsModel::ColecoVision) {
|
|
return SmsRevision::Sms1;
|
|
} else if(_model == SmsModel::GameGear) {
|
|
return SmsRevision::Sms2;
|
|
}
|
|
return _emu->GetSettings()->GetSmsConfig().Revision;
|
|
}
|
|
|
|
ConsoleRegion SmsConsole::GetRegion()
|
|
{
|
|
return _region;
|
|
}
|
|
|
|
ConsoleType SmsConsole::GetConsoleType()
|
|
{
|
|
return ConsoleType::Sms;
|
|
}
|
|
|
|
vector<CpuType> SmsConsole::GetCpuTypes()
|
|
{
|
|
return { CpuType::Sms };
|
|
}
|
|
|
|
uint64_t SmsConsole::GetMasterClock()
|
|
{
|
|
return _cpu->GetState().CycleCount;
|
|
}
|
|
|
|
uint32_t SmsConsole::GetMasterClockRate()
|
|
{
|
|
return (_model != SmsModel::GameGear && _region == ConsoleRegion::Pal) ? 3546895 : 3579545;
|
|
}
|
|
|
|
double SmsConsole::GetFps()
|
|
{
|
|
return (_model != SmsModel::GameGear && _region == ConsoleRegion::Pal) ? 49.701460 : 59.9227434;
|
|
}
|
|
|
|
BaseVideoFilter* SmsConsole::GetVideoFilter(bool getDefaultFilter)
|
|
{
|
|
if(getDefaultFilter) {
|
|
return new SmsDefaultVideoFilter(_emu, this);
|
|
}
|
|
|
|
VideoFilterType filterType = _emu->GetSettings()->GetVideoConfig().VideoFilter;
|
|
|
|
switch(filterType) {
|
|
case VideoFilterType::NtscBisqwit:
|
|
case VideoFilterType::NtscBlargg:
|
|
return new SmsNtscFilter(_emu, this);
|
|
|
|
default: return new SmsDefaultVideoFilter(_emu, this);
|
|
}
|
|
}
|
|
|
|
PpuFrameInfo SmsConsole::GetPpuFrame()
|
|
{
|
|
PpuFrameInfo frame = {};
|
|
frame.FirstScanline = 0;
|
|
frame.FrameCount = _vdp->GetFrameCount();
|
|
frame.Width = 256;
|
|
frame.Height = 240;
|
|
frame.ScanlineCount = _region == ConsoleRegion::Ntsc ? 262 : 313;
|
|
frame.CycleCount = 342;
|
|
frame.FrameBufferSize = 256*240*sizeof(uint16_t);
|
|
frame.FrameBuffer = (uint8_t*)_vdp->GetScreenBuffer(false);
|
|
return frame;
|
|
}
|
|
|
|
RomFormat SmsConsole::GetRomFormat()
|
|
{
|
|
return _romFormat;
|
|
}
|
|
|
|
AudioTrackInfo SmsConsole::GetAudioTrackInfo()
|
|
{
|
|
//TODOSMS audio player
|
|
return AudioTrackInfo();
|
|
}
|
|
|
|
void SmsConsole::ProcessAudioPlayerAction(AudioPlayerActionParams p)
|
|
{
|
|
//TODOSMS audio player
|
|
}
|
|
|
|
void SmsConsole::RefreshRamCheats()
|
|
{
|
|
//Used to refresh pro action replay codes when vertical blank IRQ is triggered
|
|
for(InternalCheatCode& code : _emu->GetCheatManager()->GetRamRefreshCheats(CpuType::Sms)) {
|
|
_memoryManager->DebugWrite(code.Address, code.Value);
|
|
}
|
|
}
|
|
|
|
AddressInfo SmsConsole::GetAbsoluteAddress(uint32_t relAddress)
|
|
{
|
|
return _memoryManager->GetAbsoluteAddress(relAddress);
|
|
}
|
|
|
|
AddressInfo SmsConsole::GetAbsoluteAddress(AddressInfo& relAddress)
|
|
{
|
|
return GetAbsoluteAddress(relAddress.Address);
|
|
}
|
|
|
|
AddressInfo SmsConsole::GetRelativeAddress(AddressInfo& absAddress, CpuType cpuType)
|
|
{
|
|
return { _memoryManager->GetRelativeAddress(absAddress), MemoryType::SmsMemory };
|
|
}
|
|
|
|
SmsState SmsConsole::GetState()
|
|
{
|
|
SmsState state;
|
|
state.Cpu = _cpu->GetState();
|
|
state.Vdp = _vdp->GetState();
|
|
state.Psg = _psg->GetState();
|
|
state.MemoryManager = _memoryManager->GetState();
|
|
state.ControlManager = _controlManager->GetState();
|
|
return state;
|
|
}
|
|
|
|
void SmsConsole::GetConsoleState(BaseState& state, ConsoleType consoleType)
|
|
{
|
|
(SmsState&)state = GetState();
|
|
}
|
|
|
|
void SmsConsole::InitializeRam(void* data, uint32_t length)
|
|
{
|
|
EmuSettings* settings = _emu->GetSettings();
|
|
RamState ramState = _model == SmsModel::ColecoVision ? settings->GetCvConfig().RamPowerOnState : settings->GetSmsConfig().RamPowerOnState;
|
|
settings->InitializeRam(ramState, data, length);
|
|
}
|
|
|
|
void SmsConsole::Serialize(Serializer& s)
|
|
{
|
|
SV(_cpu);
|
|
SV(_vdp);
|
|
SV(_controlManager);
|
|
SV(_cart); //Process cart before memory manager to ensure mappings are updated properly
|
|
SV(_memoryManager);
|
|
SV(_psg);
|
|
SV(_fmAudio);
|
|
}
|