Mesen-S/Core/Console.cpp

565 lines
14 KiB
C++
Raw Normal View History

#include "stdafx.h"
#include "Console.h"
#include "Cpu.h"
#include "Ppu.h"
#include "Spc.h"
#include "NecDsp.h"
#include "InternalRegisters.h"
2019-02-17 19:54:29 -05:00
#include "ControlManager.h"
#include "MemoryManager.h"
#include "DmaController.h"
2019-02-26 22:27:09 -05:00
#include "BaseCartridge.h"
#include "RamHandler.h"
#include "Gameboy.h"
#include "GbPpu.h"
#include "Debugger.h"
2019-03-01 20:27:49 -05:00
#include "DebugTypes.h"
2019-02-15 21:33:13 -05:00
#include "NotificationManager.h"
#include "SoundMixer.h"
#include "VideoDecoder.h"
#include "VideoRenderer.h"
#include "DebugHud.h"
#include "MessageManager.h"
#include "KeyManager.h"
2019-03-07 20:12:32 -05:00
#include "EventType.h"
#include "EmuSettings.h"
2019-03-12 09:15:57 -04:00
#include "SaveStateManager.h"
#include "CartTypes.h"
2019-03-12 12:06:42 -04:00
#include "RewindManager.h"
2019-03-12 09:15:57 -04:00
#include "ConsoleLock.h"
#include "MovieManager.h"
#include "BatteryManager.h"
2019-10-12 22:40:25 -04:00
#include "CheatManager.h"
2019-10-16 20:22:45 -04:00
#include "MovieManager.h"
#include "SystemActionManager.h"
2019-11-01 21:15:11 -04:00
#include "Msu1.h"
2019-03-12 09:15:57 -04:00
#include "../Utilities/Serializer.h"
#include "../Utilities/Timer.h"
#include "../Utilities/VirtualFile.h"
2019-02-21 17:18:56 -05:00
#include "../Utilities/PlatformUtilities.h"
2019-02-24 23:53:14 -05:00
#include "../Utilities/FolderUtilities.h"
Console::Console()
{
2019-10-20 20:05:39 -04:00
_settings.reset(new EmuSettings(this));
_paused = false;
_pauseOnNextFrame = false;
_stopFlag = false;
_lockCounter = 0;
_threadPaused = false;
}
2019-02-26 22:27:09 -05:00
Console::~Console()
{
}
void Console::Initialize()
{
2019-03-12 09:15:57 -04:00
_lockCounter = 0;
2019-02-15 21:33:13 -05:00
_notificationManager.reset(new NotificationManager());
_batteryManager.reset(new BatteryManager());
_videoDecoder.reset(new VideoDecoder(shared_from_this()));
_videoRenderer.reset(new VideoRenderer(shared_from_this()));
2019-03-12 09:15:57 -04:00
_saveStateManager.reset(new SaveStateManager(shared_from_this()));
_soundMixer.reset(new SoundMixer(this));
_debugHud.reset(new DebugHud());
2019-10-12 22:40:25 -04:00
_cheatManager.reset(new CheatManager(this));
2019-10-16 20:22:45 -04:00
_movieManager.reset(new MovieManager(shared_from_this()));
_videoDecoder->StartThread();
_videoRenderer->StartThread();
}
void Console::Release()
{
2019-03-14 18:07:25 -04:00
Stop(true);
_videoDecoder->StopThread();
_videoRenderer->StopThread();
_videoDecoder.reset();
_videoRenderer.reset();
_debugHud.reset();
_notificationManager.reset();
2019-03-12 09:15:57 -04:00
_saveStateManager.reset();
_soundMixer.reset();
_settings.reset();
2019-10-16 20:22:45 -04:00
_cheatManager.reset();
_movieManager.reset();
}
void Console::RunFrame()
{
_frameRunning = true;
if(_settings->CheckFlag(EmulationFlags::GameboyMode)) {
Gameboy* gameboy = _cart->GetGameboy();
while(_frameRunning) {
gameboy->Exec();
}
} else {
while(_frameRunning) {
_cpu->Exec();
}
}
}
void Console::ProcessEndOfFrame()
{
_frameRunning = false;
}
2019-07-02 19:56:00 -04:00
void Console::RunSingleFrame()
{
_controlManager->UpdateInputState();
_internalRegisters->ProcessAutoJoypadRead();
RunFrame();
2019-07-02 19:56:00 -04:00
2019-07-30 22:34:52 -04:00
_cart->RunCoprocessors();
if(_cart->GetCoprocessor()) {
_cart->GetCoprocessor()->ProcessEndOfFrame();
}
2019-07-02 19:56:00 -04:00
_controlManager->UpdateControlDevices();
}
2019-03-14 18:07:25 -04:00
void Console::Stop(bool sendNotification)
{
_stopFlag = true;
_notificationManager->SendNotification(ConsoleNotificationType::BeforeGameUnload);
_emulationLock.WaitForRelease();
if(sendNotification) {
_notificationManager->SendNotification(ConsoleNotificationType::BeforeEmulationStop);
}
_consoleType = ConsoleType::Snes;
_settings->ClearFlag(EmulationFlags::GameboyMode);
2019-03-08 17:08:28 -05:00
_videoDecoder->StopThread();
2019-03-12 12:06:42 -04:00
_rewindManager.reset();
2019-03-08 17:08:28 -05:00
_cpu.reset();
_ppu.reset();
_spc.reset();
2019-02-16 01:16:57 -05:00
_cart.reset();
_internalRegisters.reset();
2019-02-17 19:54:29 -05:00
_controlManager.reset();
_memoryManager.reset();
_dmaController.reset();
2019-11-01 21:15:11 -04:00
_msu1.reset();
_soundMixer->StopAudio(true);
2019-03-14 18:07:25 -04:00
if(sendNotification) {
_notificationManager->SendNotification(ConsoleNotificationType::EmulationStopped);
}
}
void Console::Reset()
{
_lockCounter++;
_runLock.Acquire();
_dmaController->Reset();
_internalRegisters->Reset();
_memoryManager->Reset();
_spc->Reset();
_ppu->Reset();
2019-07-30 22:34:52 -04:00
_cart->Reset();
//_controlManager->Reset();
//Reset cart before CPU to ensure correct memory mappings when fetching reset vector
_cpu->Reset();
_notificationManager->SendNotification(ConsoleNotificationType::GameReset);
ProcessEvent(EventType::Reset);
_runLock.Release();
_lockCounter--;
}
void Console::ReloadRom(bool forPowerCycle)
{
shared_ptr<BaseCartridge> cart = _cart;
if(cart) {
RomInfo info = cart->GetRomInfo();
Lock();
LoadRom(info.RomFile, info.PatchFile, false, forPowerCycle);
Unlock();
}
}
void Console::PowerCycle()
{
ReloadRom(true);
}
bool Console::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom, bool forPowerCycle)
{
if(_cart) {
//Make sure the battery is saved to disk before we load another game (or reload the same game)
_cart->SaveBattery();
}
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 = forPowerCycle ? _cart : BaseCartridge::CreateCartridge(this, romFile, patchFile);
if(cart) {
if(stopRom) {
KeyManager::UpdateDevices();
Stop(false);
}
_cheatManager->ClearCheats(false);
_cart = cart;
_batteryManager->Initialize(FolderUtilities::GetFilename(romFile.GetFileName(), false));
UpdateRegion();
2019-08-09 11:45:20 -04:00
_internalRegisters.reset(new InternalRegisters());
2019-04-10 20:45:59 -04:00
_memoryManager.reset(new MemoryManager());
_ppu.reset(new Ppu(this));
_controlManager.reset(new ControlManager(this));
2019-02-24 19:57:34 -05:00
_dmaController.reset(new DmaController(_memoryManager.get()));
_spc.reset(new Spc(this));
2019-11-01 21:15:11 -04:00
_msu1.reset(Msu1::Init(romFile, _spc.get()));
_cpu.reset(new Cpu(this));
_memoryManager->Initialize(this);
2019-08-09 11:45:20 -04:00
_internalRegisters->Initialize(this);
2019-02-15 00:08:50 -05:00
if(_cart->GetCoprocessor() == nullptr && _cart->GetGameboy()) {
_cart->GetGameboy()->PowerOn();
_consoleType = _cart->GetGameboy()->IsCgb() ? ConsoleType::GameboyColor : ConsoleType::Gameboy;
_settings->SetFlag(EmulationFlags::GameboyMode);
} else {
_consoleType = ConsoleType::Snes;
_settings->ClearFlag(EmulationFlags::GameboyMode);
}
_ppu->PowerOn();
_cpu->PowerOn();
_rewindManager.reset(new RewindManager(shared_from_this()));
_notificationManager->RegisterNotificationListener(_rewindManager);
_controlManager->UpdateControlDevices();
UpdateRegion();
_notificationManager->SendNotification(ConsoleNotificationType::GameLoaded, (void*)forPowerCycle);
2019-10-20 20:05:39 -04:00
2019-03-12 13:13:32 -04:00
_paused = false;
2019-03-14 18:07:25 -04:00
if(!forPowerCycle) {
string modelName = _region == ConsoleRegion::Pal ? "PAL" : "NTSC";
string messageTitle = MessageManager::Localize("GameLoaded") + " (" + modelName + ")";
MessageManager::DisplayMessage(messageTitle, FolderUtilities::GetFilename(GetRomInfo().RomFile.GetFileName(), false));
}
result = true;
} else {
MessageManager::DisplayMessage("Error", "CouldNotLoadFile", romFile.GetFileName());
2019-03-12 09:15:57 -04:00
}
2019-03-14 18:07:25 -04:00
_settings->SetEmulationConfig(orgConfig);
return result;
2019-03-12 09:15:57 -04:00
}
RomInfo Console::GetRomInfo()
{
shared_ptr<BaseCartridge> cart = _cart;
if(cart) {
return cart->GetRomInfo();
} else {
return {};
}
}
uint64_t Console::GetMasterClock()
{
if(_settings->CheckFlag(EmulationFlags::GameboyMode) && _cart->GetGameboy()) {
return _cart->GetGameboy()->GetCycleCount();
} else {
return _memoryManager->GetMasterClock();
}
}
2019-03-14 15:25:35 -04:00
uint32_t Console::GetMasterClockRate()
{
return _masterClockRate;
}
ConsoleRegion Console::GetRegion()
{
return _region;
}
ConsoleType Console::GetConsoleType()
{
return _consoleType;
}
2019-03-14 15:25:35 -04:00
void Console::UpdateRegion()
{
switch(_settings->GetEmulationConfig().Region) {
case ConsoleRegion::Auto: _region = _cart->GetRegion(); break;
2019-03-14 15:25:35 -04:00
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(_settings->CheckFlag(EmulationFlags::GameboyMode)) {
return 59.72750056960583;
} else {
if(_region == ConsoleRegion::Ntsc) {
return _settings->GetVideoConfig().IntegerFpsMode ? 60.0 : 60.0988118623484;
} else {
return _settings->GetVideoConfig().IntegerFpsMode ? 50.0 : 50.00697796826829;
}
}
}
2019-03-12 13:13:32 -04:00
void Console::Pause()
{
2022-04-09 21:15:25 +02:00
_paused = true;
2019-03-12 13:13:32 -04:00
}
void Console::Resume()
{
2022-04-09 21:15:25 +02:00
_paused = false;
2019-03-12 13:13:32 -04:00
}
bool Console::IsPaused()
{
2022-04-09 21:15:25 +02:00
return _paused;
2019-03-12 13:13:32 -04:00
}
2019-03-12 09:15:57 -04:00
ConsoleLock Console::AcquireLock()
{
return ConsoleLock(this);
}
void Console::Lock()
{
_lockCounter++;
_runLock.Acquire();
}
void Console::Unlock()
{
_runLock.Release();
_lockCounter--;
}
void Console::Serialize(ostream &out, int compressionLevel)
2019-03-12 09:15:57 -04:00
{
Serializer serializer(SaveStateManager::FileFormatVersion);
bool isGameboyMode = _settings->CheckFlag(EmulationFlags::GameboyMode);
if(!isGameboyMode) {
serializer.Stream(_cpu.get());
serializer.Stream(_memoryManager.get());
serializer.Stream(_ppu.get());
serializer.Stream(_dmaController.get());
serializer.Stream(_internalRegisters.get());
serializer.Stream(_cart.get());
serializer.Stream(_controlManager.get());
serializer.Stream(_spc.get());
if(_msu1) {
serializer.Stream(_msu1.get());
}
} else {
serializer.Stream(_cart.get());
serializer.Stream(_controlManager.get());
2019-11-01 21:15:11 -04:00
}
serializer.Save(out, compressionLevel);
2019-03-12 09:15:57 -04:00
}
void Console::Deserialize(istream &in, uint32_t fileFormatVersion, bool compressed)
2019-03-12 09:15:57 -04:00
{
Serializer serializer(in, fileFormatVersion, compressed);
bool isGameboyMode = _settings->CheckFlag(EmulationFlags::GameboyMode);
if(!isGameboyMode) {
serializer.Stream(_cpu.get());
serializer.Stream(_memoryManager.get());
serializer.Stream(_ppu.get());
serializer.Stream(_dmaController.get());
serializer.Stream(_internalRegisters.get());
serializer.Stream(_cart.get());
serializer.Stream(_controlManager.get());
serializer.Stream(_spc.get());
if(_msu1) {
serializer.Stream(_msu1.get());
}
} else {
serializer.Stream(_cart.get());
serializer.Stream(_controlManager.get());
2019-11-01 21:15:11 -04:00
}
2019-03-12 12:06:42 -04:00
_notificationManager->SendNotification(ConsoleNotificationType::StateLoaded);
2019-03-12 09:15:57 -04:00
}
shared_ptr<SoundMixer> Console::GetSoundMixer()
{
return _soundMixer;
}
shared_ptr<VideoRenderer> Console::GetVideoRenderer()
{
return _videoRenderer;
}
shared_ptr<VideoDecoder> Console::GetVideoDecoder()
{
return _videoDecoder;
}
2019-02-15 21:33:13 -05:00
shared_ptr<NotificationManager> Console::GetNotificationManager()
{
return _notificationManager;
}
shared_ptr<EmuSettings> Console::GetSettings()
{
return _settings;
}
2019-03-12 09:15:57 -04:00
shared_ptr<SaveStateManager> Console::GetSaveStateManager()
{
return _saveStateManager;
}
2019-03-12 12:06:42 -04:00
shared_ptr<RewindManager> Console::GetRewindManager()
{
return _rewindManager;
}
shared_ptr<DebugHud> Console::GetDebugHud()
{
return _debugHud;
}
shared_ptr<BatteryManager> Console::GetBatteryManager()
{
return _batteryManager;
}
2019-10-12 22:40:25 -04:00
shared_ptr<CheatManager> Console::GetCheatManager()
{
return _cheatManager;
}
2019-10-16 20:22:45 -04:00
shared_ptr<MovieManager> Console::GetMovieManager()
{
return _movieManager;
}
2019-02-13 18:44:39 -05:00
shared_ptr<Cpu> Console::GetCpu()
{
return _cpu;
}
shared_ptr<Ppu> Console::GetPpu()
{
return _ppu;
}
shared_ptr<Spc> Console::GetSpc()
{
return _spc;
}
2019-02-15 21:33:13 -05:00
shared_ptr<BaseCartridge> Console::GetCartridge()
{
return _cart;
}
shared_ptr<MemoryManager> Console::GetMemoryManager()
{
return _memoryManager;
}
shared_ptr<InternalRegisters> Console::GetInternalRegisters()
{
return _internalRegisters;
}
2019-02-17 19:54:29 -05:00
shared_ptr<ControlManager> Console::GetControlManager()
{
return _controlManager;
}
shared_ptr<DmaController> Console::GetDmaController()
{
return _dmaController;
}
2019-11-01 21:15:11 -04:00
shared_ptr<Msu1> Console::GetMsu1()
{
return _msu1;
}
2019-02-17 15:02:33 -05:00
bool Console::IsRunning()
{
return _cpu != nullptr;
}
uint32_t Console::GetFrameCount()
{
shared_ptr<BaseCartridge> cart = _cart;
if(_settings->CheckFlag(EmulationFlags::GameboyMode) && cart->GetGameboy()) {
GbPpu* ppu = cart->GetGameboy()->GetPpu();
return ppu ? ppu->GetFrameCount() : 0;
} else {
shared_ptr<Ppu> ppu = _ppu;
return ppu ? ppu->GetFrameCount() : 0;
}
}
template<CpuType type>
void Console::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi)
{
}
2019-03-07 20:12:32 -05:00
void Console::ProcessEvent(EventType type)
{
}
void Console::BreakImmediately(BreakSource source)
{
}
template void Console::ProcessMemoryRead<CpuType::Cpu>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Console::ProcessMemoryRead<CpuType::Sa1>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Console::ProcessMemoryRead<CpuType::Spc>(uint32_t addr, uint8_t value, MemoryOperationType opType);
2019-07-30 22:34:52 -04:00
template void Console::ProcessMemoryRead<CpuType::Gsu>(uint32_t addr, uint8_t value, MemoryOperationType opType);
2020-02-23 21:50:55 -05:00
template void Console::ProcessMemoryRead<CpuType::NecDsp>(uint32_t addr, uint8_t value, MemoryOperationType opType);
2020-02-24 22:00:52 -05:00
template void Console::ProcessMemoryRead<CpuType::Cx4>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Console::ProcessMemoryRead<CpuType::Gameboy>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Console::ProcessMemoryWrite<CpuType::Cpu>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Console::ProcessMemoryWrite<CpuType::Sa1>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Console::ProcessMemoryWrite<CpuType::Spc>(uint32_t addr, uint8_t value, MemoryOperationType opType);
2019-07-30 22:34:52 -04:00
template void Console::ProcessMemoryWrite<CpuType::Gsu>(uint32_t addr, uint8_t value, MemoryOperationType opType);
2020-02-23 21:50:55 -05:00
template void Console::ProcessMemoryWrite<CpuType::NecDsp>(uint32_t addr, uint8_t value, MemoryOperationType opType);
2020-02-24 22:00:52 -05:00
template void Console::ProcessMemoryWrite<CpuType::Cx4>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Console::ProcessMemoryWrite<CpuType::Gameboy>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Console::ProcessInterrupt<CpuType::Cpu>(uint32_t originalPc, uint32_t currentPc, bool forNmi);
template void Console::ProcessInterrupt<CpuType::Sa1>(uint32_t originalPc, uint32_t currentPc, bool forNmi);
template void Console::ProcessInterrupt<CpuType::Gameboy>(uint32_t originalPc, uint32_t currentPc, bool forNmi);