Mesen2/Core/SNES/Console.cpp

437 lines
10 KiB
C++

#include "stdafx.h"
#include "SNES/Console.h"
#include "SNES/Cpu.h"
#include "SNES/Ppu.h"
#include "SNES/Spc.h"
#include "SNES/InternalRegisters.h"
#include "SNES/ControlManager.h"
#include "SNES/MemoryManager.h"
#include "SNES/DmaController.h"
#include "SNES/BaseCartridge.h"
#include "SNES/RamHandler.h"
#include "SNES/CartTypes.h"
#include "SNES/SpcFileData.h"
#include "SNES/SnesDefaultVideoFilter.h"
#include "SNES/SnesNtscFilter.h"
#include "Gameboy/Gameboy.h"
#include "Gameboy/GbPpu.h"
#include "Debugger/Debugger.h"
#include "Debugger/DebugTypes.h"
#include "SNES/Coprocessors/MSU1/Msu1.h"
#include "SNES/Coprocessors/SA1/Sa1.h"
#include "SNES/Coprocessors/GSU/Gsu.h"
#include "SNES/Coprocessors/CX4/Cx4.h"
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
#include "Shared/Interfaces/IControlManager.h"
#include "Utilities/Serializer.h"
#include "Utilities/Timer.h"
#include "Utilities/VirtualFile.h"
#include "Utilities/PlatformUtilities.h"
#include "Utilities/FolderUtilities.h"
#include "EventType.h"
Console::Console(Emulator* emu)
{
_emu = emu;
_settings = emu->GetSettings();
}
Console::~Console()
{
}
void Console::Initialize()
{
}
void Console::Release()
{
}
void Console::RunFrame()
{
_frameRunning = true;
while(_frameRunning) {
_cpu->Exec();
}
}
void Console::OnBeforeRun()
{
_memoryManager->IncMasterClockStartup();
//TODO?
//_controlManager->UpdateInputState();
}
void Console::ProcessEndOfFrame()
{
#ifndef LIBRETRO
_cart->RunCoprocessors();
if(_cart->GetCoprocessor()) {
_cart->GetCoprocessor()->ProcessEndOfFrame();
}
_emu->ProcessEndOfFrame();
_controlManager->UpdateControlDevices();
_controlManager->UpdateInputState();
_internalRegisters->ProcessAutoJoypadRead();
#endif
_frameRunning = false;
}
void Console::RunSingleFrame()
{
//Used by Libretro
/*_emulationThreadId = std::this_thread::get_id();
_isRunAheadFrame = false;
_controlManager->UpdateInputState();
_internalRegisters->ProcessAutoJoypadRead();
RunFrame();
_cart->RunCoprocessors();
if(_cart->GetCoprocessor()) {
_cart->GetCoprocessor()->ProcessEndOfFrame();
}
_controlManager->UpdateControlDevices();*/
}
void Console::Stop()
{
_cpu.reset();
_ppu.reset();
_spc.reset();
_cart.reset();
_internalRegisters.reset();
_controlManager.reset();
_memoryManager.reset();
_dmaController.reset();
_msu1.reset();
}
void Console::Reset()
{
_dmaController->Reset();
_internalRegisters->Reset();
_memoryManager->Reset();
_spc->Reset();
_ppu->Reset();
_cart->Reset();
//_controlManager->Reset();
//Reset cart before CPU to ensure correct memory mappings when fetching reset vector
_cpu->Reset();
}
bool Console::LoadRom(VirtualFile& romFile)
{
bool result = false;
EmulationConfig orgConfig = _settings->GetEmulationConfig(); //backup emulation config (can be temporarily overriden to control the power on RAM state)
shared_ptr<BaseCartridge> cart = BaseCartridge::CreateCartridge(this, romFile);
if(cart) {
_cart = cart;
UpdateRegion();
_internalRegisters.reset(new InternalRegisters());
_memoryManager.reset(new MemoryManager());
_ppu.reset(new Ppu(_emu, this));
_controlManager.reset(new ControlManager(this));
_dmaController.reset(new DmaController(_memoryManager.get()));
_spc.reset(new Spc(this));
_msu1.reset(Msu1::Init(romFile, _spc.get()));
if(_cart->GetSpcData()) {
//TODO
_spc->LoadSpcFile(_cart->GetSpcData());
}
_cpu.reset(new Cpu(this));
_memoryManager->Initialize(this);
_internalRegisters->Initialize(this);
_ppu->PowerOn();
_cpu->PowerOn();
_controlManager->UpdateControlDevices();
UpdateRegion();
result = true;
}
_settings->SetEmulationConfig(orgConfig);
return result;
}
void Console::Init()
{
}
uint64_t Console::GetMasterClock()
{
return _memoryManager->GetMasterClock();
}
uint32_t Console::GetMasterClockRate()
{
return _masterClockRate;
}
ConsoleRegion Console::GetRegion()
{
return _region;
}
ConsoleType Console::GetConsoleType()
{
return ConsoleType::Snes;
}
void Console::UpdateRegion()
{
switch(_settings->GetSnesConfig().Region) {
case ConsoleRegion::Auto: _region = _cart->GetRegion(); break;
default:
case ConsoleRegion::Ntsc: _region = ConsoleRegion::Ntsc; break;
case ConsoleRegion::Pal: _region = ConsoleRegion::Pal; break;
}
_masterClockRate = _region == ConsoleRegion::Pal ? 21281370 : 21477270;
}
double Console::GetFps()
{
if(_region == ConsoleRegion::Ntsc) {
return _settings->GetVideoConfig().IntegerFpsMode ? 60.0 : 60.0988118623484;
} else {
return _settings->GetVideoConfig().IntegerFpsMode ? 50.0 : 50.00697796826829;
}
}
PpuFrameInfo Console::GetPpuFrame()
{
//TODO null checks
PpuFrameInfo frame;
frame.FrameBuffer = (uint8_t*)_ppu->GetScreenBuffer();
frame.Width = 256;
frame.Height = 239;
frame.FrameCount = _ppu->GetFrameCount();
return frame;
}
vector<CpuType> Console::GetCpuTypes()
{
return { CpuType::Cpu, CpuType::Spc };
}
void Console::SaveBattery()
{
if(_cart) {
_cart->SaveBattery();
}
}
BaseVideoFilter* Console::GetVideoFilter()
{
VideoFilterType filterType = _emu->GetSettings()->GetVideoConfig().VideoFilter;
if(filterType == VideoFilterType::NTSC) {
return new SnesNtscFilter(_emu);
} else {
return new SnesDefaultVideoFilter(_emu);
}
}
RomFormat Console::GetRomFormat()
{
return _cart->GetSpcData() ? RomFormat::Spc : RomFormat::Sfc;
}
AudioTrackInfo Console::GetAudioTrackInfo()
{
AudioTrackInfo track = {};
SpcFileData* spc = _cart->GetSpcData();
if(spc) {
track.Artist = spc->Artist;
track.Comment = spc->Comment;
track.GameTitle = spc->GameTitle;
track.SongTitle = spc->SongTitle;
track.Position = _ppu->GetFrameCount() / GetFps();
track.Length = -1;
}
return track;
}
double Console::GetFrameDelay()
{
UpdateRegion();
switch(_region) {
default:
case ConsoleRegion::Ntsc: return _settings->GetVideoConfig().IntegerFpsMode ? 16.6666666666666666667 : 16.63926405550947;
case ConsoleRegion::Pal: return _settings->GetVideoConfig().IntegerFpsMode ? 20 : 19.99720882631146;
}
}
void Console::Serialize(Serializer& s)
{
s.Stream(_cpu.get());
s.Stream(_memoryManager.get());
s.Stream(_ppu.get());
s.Stream(_dmaController.get());
s.Stream(_internalRegisters.get());
s.Stream(_cart.get());
s.Stream(_controlManager.get());
s.Stream(_spc.get());
if(_msu1) {
s.Stream(_msu1.get());
}
}
shared_ptr<Cpu> Console::GetCpu()
{
return _cpu;
}
shared_ptr<Ppu> Console::GetPpu()
{
return _ppu;
}
shared_ptr<Spc> Console::GetSpc()
{
return _spc;
}
shared_ptr<BaseCartridge> Console::GetCartridge()
{
return _cart;
}
shared_ptr<MemoryManager> Console::GetMemoryManager()
{
return _memoryManager;
}
shared_ptr<InternalRegisters> Console::GetInternalRegisters()
{
return _internalRegisters;
}
shared_ptr<IControlManager> Console::GetControlManager()
{
return _controlManager;
}
shared_ptr<DmaController> Console::GetDmaController()
{
return _dmaController;
}
shared_ptr<Msu1> Console::GetMsu1()
{
return _msu1;
}
Emulator* Console::GetEmulator()
{
return _emu;
}
bool Console::IsRunning()
{
return _cpu != nullptr;
}
AddressInfo Console::GetAbsoluteAddress(AddressInfo relAddress)
{
if(relAddress.Type == SnesMemoryType::CpuMemory) {
if(_memoryManager->IsRegister(relAddress.Address)) {
return { relAddress.Address & 0xFFFF, SnesMemoryType::Register };
} else {
return _memoryManager->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address);
}
} else if(relAddress.Type == SnesMemoryType::SpcMemory) {
return _spc->GetAbsoluteAddress(relAddress.Address);
} else if(relAddress.Type == SnesMemoryType::Sa1Memory) {
return _cart->GetSa1()->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address);
} else if(relAddress.Type == SnesMemoryType::GsuMemory) {
return _cart->GetGsu()->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address);
} else if(relAddress.Type == SnesMemoryType::Cx4Memory) {
return _cart->GetCx4()->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address);
} else if(relAddress.Type == SnesMemoryType::NecDspMemory) {
return { relAddress.Address, SnesMemoryType::DspProgramRom };
} else if(relAddress.Type == SnesMemoryType::GameboyMemory) {
return _cart->GetGameboy()->GetAbsoluteAddress(relAddress.Address);
}
throw std::runtime_error("Unsupported address type");
}
AddressInfo Console::GetRelativeAddress(AddressInfo absAddress, CpuType cpuType)
{
MemoryMappings* mappings = nullptr;
switch(cpuType) {
case CpuType::Cpu: mappings = _memoryManager->GetMemoryMappings(); break;
case CpuType::Spc: break;
case CpuType::NecDsp: break;
case CpuType::Sa1: mappings = _cart->GetSa1()->GetMemoryMappings(); break;
case CpuType::Gsu: mappings = _cart->GetGsu()->GetMemoryMappings(); break;
case CpuType::Cx4: mappings = _cart->GetCx4()->GetMemoryMappings(); break;
case CpuType::Gameboy: break;
}
switch(absAddress.Type) {
case SnesMemoryType::PrgRom:
case SnesMemoryType::WorkRam:
case SnesMemoryType::SaveRam:
{
if(!mappings) {
throw std::runtime_error("Unsupported cpu type");
}
uint8_t startBank = 0;
//Try to find a mirror close to where the PC is
if(cpuType == CpuType::Cpu) {
if(absAddress.Type == SnesMemoryType::WorkRam) {
startBank = 0x7E;
} else {
startBank = _cpu->GetState().K & 0xC0;
}
} else if(cpuType == CpuType::Sa1) {
startBank = (_cart->GetSa1()->GetCpuState().K & 0xC0);
} else if(cpuType == CpuType::Gsu) {
startBank = (_cart->GetGsu()->GetState().ProgramBank & 0xC0);
}
return { mappings->GetRelativeAddress(absAddress, startBank), DebugUtilities::GetCpuMemoryType(cpuType) };
}
case SnesMemoryType::SpcRam:
case SnesMemoryType::SpcRom:
return { _spc->GetRelativeAddress(absAddress), SnesMemoryType::SpcMemory };
case SnesMemoryType::GbPrgRom:
case SnesMemoryType::GbWorkRam:
case SnesMemoryType::GbCartRam:
case SnesMemoryType::GbHighRam:
case SnesMemoryType::GbBootRom:
return { _cart->GetGameboy()->GetRelativeAddress(absAddress), SnesMemoryType::GameboyMemory };
case SnesMemoryType::DspProgramRom:
return { absAddress.Address, SnesMemoryType::NecDspMemory };
case SnesMemoryType::Register:
return { absAddress.Address & 0xFFFF, SnesMemoryType::Register };
default:
return { -1, SnesMemoryType::Register };
}
}