mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
723 lines
No EOL
20 KiB
C++
723 lines
No EOL
20 KiB
C++
#include "pch.h"
|
|
#include "NES/NesConsole.h"
|
|
#include "NES/NesControlManager.h"
|
|
#include "NES/MapperFactory.h"
|
|
#include "NES/APU/NesApu.h"
|
|
#include "NES/NesCpu.h"
|
|
#include "NES/BaseMapper.h"
|
|
#include "NES/NesSoundMixer.h"
|
|
#include "NES/NesMemoryManager.h"
|
|
#include "NES/DefaultNesPpu.h"
|
|
#include "NES/NsfPpu.h"
|
|
#include "NES/HdPacks/HdAudioDevice.h"
|
|
#include "NES/HdPacks/HdData.h"
|
|
#include "NES/HdPacks/HdNesPpu.h"
|
|
#include "NES/HdPacks/HdPackLoader.h"
|
|
#include "NES/HdPacks/HdPackBuilder.h"
|
|
#include "NES/HdPacks/HdBuilderPpu.h"
|
|
#include "NES/HdPacks/HdVideoFilter.h"
|
|
#include "NES/NesDefaultVideoFilter.h"
|
|
#include "NES/NesNtscFilter.h"
|
|
#include "NES/BisqwitNtscFilter.h"
|
|
#include "NES/NesConstants.h"
|
|
#include "NES/Epsm.h"
|
|
#include "NES/Mappers/VsSystem/VsControlManager.h"
|
|
#include "NES/Mappers/NSF/NsfMapper.h"
|
|
#include "NES/Mappers/FDS/Fds.h"
|
|
#include "Shared/Emulator.h"
|
|
#include "Shared/Audio/SoundMixer.h"
|
|
#include "Shared/SaveStateManager.h"
|
|
#include "Shared/CheatManager.h"
|
|
#include "Shared/Movies/MovieManager.h"
|
|
#include "Shared/BaseControlManager.h"
|
|
#include "Shared/Interfaces/IBattery.h"
|
|
#include "Shared/EmuSettings.h"
|
|
#include "Shared/NotificationManager.h"
|
|
#include "Netplay/GameClient.h"
|
|
#include "Debugger/DebugTypes.h"
|
|
#include "Utilities/Serializer.h"
|
|
#include "Utilities/sha1.h"
|
|
|
|
NesConsole::NesConsole(Emulator* emu)
|
|
{
|
|
_emu = emu;
|
|
}
|
|
|
|
NesConsole::~NesConsole()
|
|
{
|
|
shared_ptr<HdPackData> hdData = _hdData.lock();
|
|
if(hdData) {
|
|
hdData->CancelLoad();
|
|
}
|
|
}
|
|
|
|
Emulator* NesConsole::GetEmulator()
|
|
{
|
|
return _emu;
|
|
}
|
|
|
|
NesConfig& NesConsole::GetNesConfig()
|
|
{
|
|
return _emu->GetSettings()->GetNesConfig();
|
|
}
|
|
|
|
void NesConsole::ProcessCpuClock()
|
|
{
|
|
if(_mapper->HasCpuClockHook()) {
|
|
_mapper->ProcessCpuClock();
|
|
}
|
|
|
|
_apu->ProcessCpuClock();
|
|
if(_controlManager->HasPendingWrites()) {
|
|
_controlManager->ProcessWrites();
|
|
}
|
|
}
|
|
|
|
Epsm* NesConsole::GetEpsm()
|
|
{
|
|
return _mapper->GetEpsm();
|
|
}
|
|
|
|
NesConsole* NesConsole::GetVsMainConsole()
|
|
{
|
|
return _vsMainConsole;
|
|
}
|
|
|
|
NesConsole* NesConsole::GetVsSubConsole()
|
|
{
|
|
return _vsSubConsole.get();
|
|
}
|
|
|
|
bool NesConsole::IsVsMainConsole()
|
|
{
|
|
return _vsMainConsole == nullptr;
|
|
}
|
|
|
|
void NesConsole::Serialize(Serializer& s)
|
|
{
|
|
SV(_cpu);
|
|
SV(_ppu);
|
|
SV(_memoryManager);
|
|
SV(_apu);
|
|
SV(_mapper);
|
|
|
|
if(s.GetFormat() != SerializeFormat::Map) {
|
|
SV(_mixer);
|
|
}
|
|
|
|
if(_hdAudioDevice) {
|
|
//For HD packs), save the state of the bgm playback
|
|
SV(_hdAudioDevice);
|
|
}
|
|
|
|
if(_vsSubConsole) {
|
|
//For VS Dualsystem, the sub console's savestate is appended to the end of the file
|
|
SV(_vsSubConsole);
|
|
}
|
|
|
|
SV(_controlManager);
|
|
|
|
if(!s.IsSaving()) {
|
|
UpdateRegion(true);
|
|
}
|
|
}
|
|
|
|
void NesConsole::Reset()
|
|
{
|
|
_memoryManager->Reset(true);
|
|
|
|
_ppu->Reset(true);
|
|
_apu->Reset(true);
|
|
_cpu->Reset(true, _region);
|
|
_controlManager->Reset(true);
|
|
_mixer->Reset();
|
|
if(_vsSubConsole) {
|
|
_vsSubConsole->Reset();
|
|
}
|
|
_mapper->OnAfterResetPowerOn();
|
|
}
|
|
|
|
LoadRomResult NesConsole::LoadRom(VirtualFile& romFile)
|
|
{
|
|
RomData romData;
|
|
|
|
LoadHdPack(romFile);
|
|
|
|
LoadRomResult result = LoadRomResult::UnknownType;
|
|
unique_ptr<BaseMapper> mapper = MapperFactory::InitializeFromFile(this, romFile, romData, result);
|
|
if(mapper) {
|
|
if(!_vsMainConsole && romData.Info.VsType == VsSystemType::VsDualSystem) {
|
|
//Create 2nd console (sub) dualsystem games
|
|
_vsSubConsole.reset(new NesConsole(_emu));
|
|
_vsSubConsole->_vsMainConsole = this;
|
|
result = _vsSubConsole->LoadRom(romFile);
|
|
if(result != LoadRomResult::Success) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if(GetNesConfig().AutoConfigureInput && romData.Info.InputType != GameInputType::Unspecified) {
|
|
//Auto-configure the inputs (if option is enabled)
|
|
InitializeInputDevices(romData.Info.InputType, romData.Info.System);
|
|
}
|
|
|
|
_mapper.swap(mapper);
|
|
_mixer.reset(new NesSoundMixer(this));
|
|
_memoryManager.reset(new NesMemoryManager(this, _mapper.get()));
|
|
_cpu.reset(new NesCpu(this));
|
|
_apu.reset(new NesApu(this));
|
|
|
|
if(romData.Info.System == GameSystem::VsSystem) {
|
|
_controlManager.reset(new VsControlManager(this));
|
|
} else {
|
|
_controlManager.reset(new NesControlManager(this));
|
|
}
|
|
|
|
if(_hdData) {
|
|
_ppu.reset(new HdNesPpu(this, _hdData.get()));
|
|
} else if(dynamic_cast<NsfMapper*>(_mapper.get())) {
|
|
//Disable most of the PPU for NSFs
|
|
_ppu.reset(new NsfPpu(this));
|
|
} else {
|
|
_ppu.reset(new DefaultNesPpu(this));
|
|
}
|
|
|
|
_mapper->InitSpecificMapper(romData);
|
|
|
|
if(_mapper->GetEpsm()) {
|
|
_memoryManager->RegisterIODevice(_mapper->GetEpsm());
|
|
}
|
|
_memoryManager->RegisterIODevice(_ppu.get());
|
|
_memoryManager->RegisterIODevice(_apu.get());
|
|
_memoryManager->RegisterIODevice(_controlManager.get());
|
|
_memoryManager->RegisterIODevice(_mapper.get());
|
|
|
|
if(_hdData) {
|
|
_hdAudioDevice.reset(new HdAudioDevice(_emu, _hdData.get()));
|
|
_memoryManager->RegisterIODevice(_hdAudioDevice.get());
|
|
} else {
|
|
_hdAudioDevice.reset();
|
|
}
|
|
|
|
UpdateRegion();
|
|
|
|
_mixer->Reset();
|
|
|
|
_ppu->Reset(false);
|
|
_apu->Reset(false);
|
|
_memoryManager->Reset(false);
|
|
_controlManager->Reset(false);
|
|
_cpu->Reset(false, _region);
|
|
_mapper->OnAfterResetPowerOn();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void NesConsole::LoadHdPack(VirtualFile& romFile)
|
|
{
|
|
_hdData.reset();
|
|
if(GetNesConfig().EnableHdPacks) {
|
|
_hdData.reset(new HdPackData());
|
|
if(!HdPackLoader::LoadHdNesPack(romFile, *_hdData.get())) {
|
|
_hdData.reset();
|
|
} else {
|
|
auto result = _hdData->PatchesByHash.find(romFile.GetSha1Hash());
|
|
if(result != _hdData->PatchesByHash.end()) {
|
|
VirtualFile patchFile = result->second;
|
|
romFile.ApplyPatch(patchFile);
|
|
}
|
|
|
|
shared_ptr<HdPackData> data = _hdData.lock();
|
|
if(data) {
|
|
thread asyncLoadData([data]() {
|
|
data->LoadAsync();
|
|
});
|
|
asyncLoadData.detach();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void NesConsole::UpdateRegion(bool forceUpdate)
|
|
{
|
|
ConsoleRegion region = GetNesConfig().Region;
|
|
if(region == ConsoleRegion::Auto) {
|
|
switch(_mapper->GetRomInfo().System) {
|
|
case GameSystem::NesPal: region = ConsoleRegion::Pal; break;
|
|
case GameSystem::Dendy: region = ConsoleRegion::Dendy; break;
|
|
default: region = ConsoleRegion::Ntsc; break;
|
|
}
|
|
}
|
|
|
|
if(_vsSubConsole) {
|
|
_vsSubConsole->UpdateRegion(forceUpdate);
|
|
}
|
|
|
|
if(_region != region || forceUpdate) {
|
|
_region = region;
|
|
|
|
_cpu->SetMasterClockDivider(_region);
|
|
_mapper->SetRegion(_region);
|
|
_ppu->UpdateTimings(_region);
|
|
_apu->SetRegion(_region);
|
|
_mixer->SetRegion(_region);
|
|
}
|
|
}
|
|
|
|
void NesConsole::RunFrame()
|
|
{
|
|
UpdateRegion();
|
|
|
|
uint32_t frame = _ppu->GetFrameCount();
|
|
|
|
if(_nextFrameOverclockDisabled) {
|
|
//Disable overclocking for the next frame
|
|
//This is used by the DMC when a sample is playing
|
|
_ppu->UpdateTimings(_region, false);
|
|
_nextFrameOverclockDisabled = false;
|
|
}
|
|
|
|
while(frame == _ppu->GetFrameCount()) {
|
|
_cpu->Exec();
|
|
if(_vsSubConsole) {
|
|
RunVsSubConsole();
|
|
}
|
|
}
|
|
|
|
_apu->EndFrame();
|
|
|
|
if(!_nextFrameOverclockDisabled) {
|
|
//Re-update timings to allow overclocking
|
|
_ppu->UpdateTimings(_region, true);
|
|
}
|
|
}
|
|
|
|
void NesConsole::RunVsSubConsole()
|
|
{
|
|
int64_t cycleGap;
|
|
while(true) {
|
|
//Run the sub console until it catches up to the main CPU
|
|
cycleGap = (int64_t)(_cpu->GetCycleCount() - _vsSubConsole->_cpu->GetCycleCount());
|
|
if(cycleGap > 5 || _ppu->GetFrameCount() > _vsSubConsole->_ppu->GetFrameCount()) {
|
|
_vsSubConsole->_cpu->Exec();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void NesConsole::SetNextFrameOverclockStatus(bool disabled)
|
|
{
|
|
//Disable overclocking for the next frame
|
|
//This is used by the DMC when a sample is playing
|
|
_nextFrameOverclockDisabled = disabled;
|
|
}
|
|
|
|
BaseControlManager* NesConsole::GetControlManager()
|
|
{
|
|
return _controlManager.get();
|
|
}
|
|
|
|
double NesConsole::GetFps()
|
|
{
|
|
UpdateRegion();
|
|
if(_region == ConsoleRegion::Ntsc) {
|
|
return 60.0988118623484;
|
|
} else {
|
|
return 50.0069789081886;
|
|
}
|
|
}
|
|
|
|
PpuFrameInfo NesConsole::GetPpuFrame()
|
|
{
|
|
PpuFrameInfo frame;
|
|
frame.FrameBuffer = (uint8_t*)_ppu->GetScreenBuffer(false);
|
|
frame.Width = NesConstants::ScreenWidth;
|
|
frame.Height = NesConstants::ScreenHeight;
|
|
frame.FrameBufferSize = frame.Width * frame.Height * sizeof(uint16_t);
|
|
frame.FrameCount = _ppu->GetFrameCount();
|
|
frame.FirstScanline = -1;
|
|
frame.ScanlineCount = _ppu->GetScanlineCount();
|
|
frame.CycleCount = 341;
|
|
return frame;
|
|
}
|
|
|
|
ConsoleType NesConsole::GetConsoleType()
|
|
{
|
|
return ConsoleType::Nes;
|
|
}
|
|
|
|
ConsoleRegion NesConsole::GetRegion()
|
|
{
|
|
return _region;
|
|
}
|
|
|
|
vector<CpuType> NesConsole::GetCpuTypes()
|
|
{
|
|
return { CpuType::Nes };
|
|
}
|
|
|
|
AddressInfo NesConsole::GetAbsoluteAddress(AddressInfo& relAddress)
|
|
{
|
|
if(relAddress.Type == MemoryType::NesMemory) {
|
|
return _mapper->GetAbsoluteAddress(relAddress.Address);
|
|
} else {
|
|
return _mapper->GetPpuAbsoluteAddress(relAddress.Address);
|
|
}
|
|
}
|
|
|
|
AddressInfo NesConsole::GetRelativeAddress(AddressInfo& absAddress, CpuType cpuType)
|
|
{
|
|
return _mapper->GetRelativeAddress(absAddress);
|
|
}
|
|
|
|
void NesConsole::GetConsoleState(BaseState& baseState, ConsoleType consoleType)
|
|
{
|
|
NesState& state = (NesState&)baseState;
|
|
|
|
state.ClockRate = GetMasterClockRate();
|
|
state.Cpu = _cpu->GetState();
|
|
_ppu->GetState(state.Ppu);
|
|
state.Cartridge = _mapper->GetState();
|
|
state.Apu = _apu->GetState();
|
|
}
|
|
|
|
uint64_t NesConsole::GetMasterClock()
|
|
{
|
|
return _cpu->GetCycleCount();
|
|
}
|
|
|
|
uint32_t NesConsole::GetMasterClockRate()
|
|
{
|
|
return NesConstants::GetClockRate(_region);
|
|
}
|
|
|
|
void NesConsole::SaveBattery()
|
|
{
|
|
if(_mapper) {
|
|
_mapper->SaveBattery();
|
|
}
|
|
|
|
if(_controlManager) {
|
|
_controlManager->SaveBattery();
|
|
}
|
|
}
|
|
|
|
ShortcutState NesConsole::IsShortcutAllowed(EmulatorShortcut shortcut, uint32_t shortcutParam)
|
|
{
|
|
bool isRunning = _emu->IsRunning();
|
|
bool isNetplayClient = _emu->GetGameClient()->Connected();
|
|
bool isMoviePlaying = _emu->GetMovieManager()->Playing();
|
|
RomFormat romFormat = GetRomFormat();
|
|
|
|
switch(shortcut) {
|
|
case EmulatorShortcut::FdsEjectDisk:
|
|
case EmulatorShortcut::FdsInsertNextDisk:
|
|
case EmulatorShortcut::FdsSwitchDiskSide:
|
|
return (ShortcutState)(isRunning && !isNetplayClient && !isMoviePlaying && romFormat == RomFormat::Fds);
|
|
|
|
case EmulatorShortcut::FdsInsertDiskNumber:
|
|
if(isRunning && !isNetplayClient && !isMoviePlaying && romFormat == RomFormat::Fds) {
|
|
Fds* fds = dynamic_cast<Fds*>(_mapper.get());
|
|
return (ShortcutState)(fds && shortcutParam < fds->GetSideCount());
|
|
}
|
|
return ShortcutState::Disabled;
|
|
|
|
case EmulatorShortcut::VsInsertCoin1:
|
|
case EmulatorShortcut::VsInsertCoin2:
|
|
case EmulatorShortcut::VsServiceButton:
|
|
return (ShortcutState)(isRunning && !isNetplayClient && !isMoviePlaying && (romFormat == RomFormat::VsSystem || romFormat == RomFormat::VsDualSystem));
|
|
|
|
case EmulatorShortcut::VsInsertCoin3:
|
|
case EmulatorShortcut::VsInsertCoin4:
|
|
case EmulatorShortcut::VsServiceButton2:
|
|
return (ShortcutState)(isRunning && !isNetplayClient && !isMoviePlaying && romFormat == RomFormat::VsDualSystem);
|
|
}
|
|
|
|
return ShortcutState::Default;
|
|
}
|
|
|
|
BaseVideoFilter* NesConsole::GetVideoFilter(bool getDefaultFilter)
|
|
{
|
|
if(getDefaultFilter || GetRomFormat() == RomFormat::Nsf) {
|
|
return new NesDefaultVideoFilter(_emu);
|
|
} else if(_hdData && !_hdPackBuilder) {
|
|
return new HdVideoFilter(this, _emu, _hdData.get());
|
|
} else {
|
|
VideoFilterType filterType = _emu->GetSettings()->GetVideoConfig().VideoFilter;
|
|
|
|
switch(filterType) {
|
|
case VideoFilterType::NtscBlargg: return new NesNtscFilter(_emu);
|
|
case VideoFilterType::NtscBisqwit: return new BisqwitNtscFilter(_emu);
|
|
default: return new NesDefaultVideoFilter(_emu);
|
|
}
|
|
}
|
|
}
|
|
|
|
string NesConsole::GetHash(HashType hashType)
|
|
{
|
|
if(hashType == HashType::Sha1Cheat) {
|
|
ConsoleMemoryInfo prgRom = _emu->GetMemory(MemoryType::NesPrgRom);
|
|
if(prgRom.Size && prgRom.Memory) {
|
|
return SHA1::GetHash((uint8_t*)prgRom.Memory, prgRom.Size);
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
RomFormat NesConsole::GetRomFormat()
|
|
{
|
|
return _mapper->GetRomInfo().Format;
|
|
}
|
|
|
|
AudioTrackInfo NesConsole::GetAudioTrackInfo()
|
|
{
|
|
NsfMapper* nsfMapper = dynamic_cast<NsfMapper*>(_mapper.get());
|
|
if(nsfMapper) {
|
|
return nsfMapper->GetAudioTrackInfo();
|
|
}
|
|
return {};
|
|
}
|
|
|
|
void NesConsole::ProcessAudioPlayerAction(AudioPlayerActionParams p)
|
|
{
|
|
NsfMapper* nsfMapper = dynamic_cast<NsfMapper*>(_mapper.get());
|
|
if(nsfMapper) {
|
|
return nsfMapper->ProcessAudioPlayerAction(p);
|
|
}
|
|
}
|
|
|
|
uint8_t NesConsole::DebugRead(uint16_t addr)
|
|
{
|
|
return _memoryManager->DebugRead(addr);
|
|
}
|
|
|
|
void NesConsole::DebugWrite(uint16_t addr, uint8_t value, bool disableSideEffects)
|
|
{
|
|
_memoryManager->DebugWrite(addr, value, disableSideEffects);
|
|
}
|
|
|
|
uint8_t NesConsole::DebugReadVram(uint16_t addr)
|
|
{
|
|
if(addr >= 0x3F00) {
|
|
return _ppu->ReadPaletteRam(addr);
|
|
} else {
|
|
return _mapper->DebugReadVram(addr);
|
|
}
|
|
}
|
|
|
|
void NesConsole::DebugWriteVram(uint16_t addr, uint8_t value)
|
|
{
|
|
if(addr >= 0x3F00) {
|
|
_ppu->WritePaletteRam(addr, value);
|
|
} else {
|
|
_mapper->DebugWriteVram(addr, value);
|
|
}
|
|
}
|
|
|
|
void NesConsole::InitializeInputDevices(GameInputType inputType, GameSystem system)
|
|
{
|
|
ControllerType port1 = ControllerType::NesController;
|
|
ControllerType port2 = ControllerType::NesController;
|
|
ControllerType expDevice = ControllerType::None;
|
|
|
|
auto log = [](string text) {
|
|
MessageManager::Log(text);
|
|
};
|
|
|
|
bool isFamicom = (system == GameSystem::Famicom || system == GameSystem::FDS || system == GameSystem::Dendy);
|
|
|
|
if(inputType == GameInputType::VsZapper) {
|
|
//VS Duck Hunt, etc. need the zapper in the first port
|
|
log("[Input] VS Zapper connected");
|
|
port1 = ControllerType::NesZapper;
|
|
} else if(inputType == GameInputType::Zapper) {
|
|
log("[Input] Zapper connected");
|
|
if(isFamicom) {
|
|
expDevice = ControllerType::FamicomZapper;
|
|
} else {
|
|
port2 = ControllerType::NesZapper;
|
|
}
|
|
} else if(inputType == GameInputType::FourScore) {
|
|
log("[Input] Four score connected");
|
|
port1 = ControllerType::FourScore;
|
|
port2 = ControllerType::FourScore;
|
|
} else if(inputType == GameInputType::FourPlayerAdapter) {
|
|
log("[Input] Four player adapter connected");
|
|
expDevice = ControllerType::TwoPlayerAdapter;
|
|
} else if(inputType == GameInputType::ArkanoidControllerFamicom) {
|
|
log("[Input] Arkanoid controller (Famicom) connected");
|
|
expDevice = ControllerType::FamicomArkanoidController;
|
|
} else if(inputType == GameInputType::ArkanoidControllerNes) {
|
|
log("[Input] Arkanoid controller (NES) connected");
|
|
port2 = ControllerType::NesArkanoidController;
|
|
} else if(inputType == GameInputType::DoubleArkanoidController) {
|
|
log("[Input] 2 arkanoid controllers (NES) connected");
|
|
port1 = ControllerType::NesArkanoidController;
|
|
port2 = ControllerType::NesArkanoidController;
|
|
} else if(inputType == GameInputType::OekaKidsTablet) {
|
|
log("[Input] Oeka Kids Tablet connected");
|
|
expDevice = ControllerType::OekaKidsTablet;
|
|
} else if(inputType == GameInputType::KonamiHyperShot) {
|
|
log("[Input] Konami Hyper Shot connected");
|
|
expDevice = ControllerType::KonamiHyperShot;
|
|
} else if(inputType == GameInputType::FamilyBasicKeyboard) {
|
|
log("[Input] Family Basic Keyboard connected");
|
|
expDevice = ControllerType::FamilyBasicKeyboard;
|
|
} else if(inputType == GameInputType::PartyTap) {
|
|
log("[Input] Party Tap connected");
|
|
expDevice = ControllerType::PartyTap;
|
|
} else if(inputType == GameInputType::PachinkoController) {
|
|
log("[Input] Pachinko controller connected");
|
|
expDevice = ControllerType::Pachinko;
|
|
} else if(inputType == GameInputType::ExcitingBoxing) {
|
|
log("[Input] Exciting Boxing controller connected");
|
|
expDevice = ControllerType::ExcitingBoxing;
|
|
} else if(inputType == GameInputType::SuborKeyboardMouse1) {
|
|
log("[Input] Subor mouse connected");
|
|
log("[Input] Subor keyboard connected");
|
|
expDevice = ControllerType::SuborKeyboard;
|
|
port2 = ControllerType::SuborMouse;
|
|
} else if(inputType == GameInputType::JissenMahjong) {
|
|
log("[Input] Jissen Mahjong controller connected");
|
|
expDevice = ControllerType::JissenMahjong;
|
|
} else if(inputType == GameInputType::BarcodeBattler) {
|
|
log("[Input] Barcode Battler barcode reader connected");
|
|
expDevice = ControllerType::BarcodeBattler;
|
|
} else if(inputType == GameInputType::BandaiHypershot) {
|
|
log("[Input] Bandai Hyper Shot gun connected");
|
|
expDevice = ControllerType::BandaiHyperShot;
|
|
} else if(inputType == GameInputType::BattleBox) {
|
|
log("[Input] Battle Box connected");
|
|
expDevice = ControllerType::BattleBox;
|
|
} else if(inputType == GameInputType::TurboFile) {
|
|
log("[Input] Ascii Turbo File connected");
|
|
expDevice = ControllerType::AsciiTurboFile;
|
|
} else if(inputType == GameInputType::FamilyTrainerSideA) {
|
|
log("[Input] Family Trainer mat connected (Side A)");
|
|
expDevice = ControllerType::FamilyTrainerMatSideA;
|
|
} else if(inputType == GameInputType::FamilyTrainerSideB) {
|
|
log("[Input] Family Trainer mat connected (Side B)");
|
|
expDevice = ControllerType::FamilyTrainerMatSideB;
|
|
} else if(inputType == GameInputType::PowerPadSideA) {
|
|
log("[Input] Power Pad connected (Side A)");
|
|
port2 = ControllerType::PowerPadSideA;
|
|
} else if(inputType == GameInputType::PowerPadSideB) {
|
|
log("[Input] Power Pad connected (Side B)");
|
|
port2 = ControllerType::PowerPadSideB;
|
|
} else if(inputType == GameInputType::SnesControllers) {
|
|
log("[Input] 2 SNES controllers connected");
|
|
port1 = ControllerType::SnesController;
|
|
port2 = ControllerType::SnesController;
|
|
} else {
|
|
log("[Input] 2 NES controllers connected");
|
|
}
|
|
|
|
isFamicom = (system == GameSystem::Famicom || system == GameSystem::FDS || system == GameSystem::Dendy);
|
|
|
|
NesConfig& cfg = GetNesConfig();
|
|
cfg.Port1.Type = port1;
|
|
cfg.Port2.Type = port2;
|
|
cfg.ExpPort.Type = expDevice;
|
|
|
|
if(port1 == ControllerType::FourScore) {
|
|
cfg.Port1SubPorts[0].Type = ControllerType::NesController;
|
|
cfg.Port1SubPorts[1].Type = ControllerType::NesController;
|
|
cfg.Port1SubPorts[2].Type = ControllerType::NesController;
|
|
cfg.Port1SubPorts[3].Type = ControllerType::NesController;
|
|
} else if(expDevice == ControllerType::TwoPlayerAdapter) {
|
|
cfg.ExpPortSubPorts[0].Type = ControllerType::NesController;
|
|
cfg.ExpPortSubPorts[1].Type = ControllerType::NesController;
|
|
}
|
|
_emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::RequestConfigChange);
|
|
}
|
|
|
|
void NesConsole::ProcessCheatCode(InternalCheatCode& code, uint32_t addr, uint8_t& value)
|
|
{
|
|
if(code.Type == CheatType::NesGameGenie && addr >= 0xC020) {
|
|
if(GetNesConfig().DisableGameGenieBusConflicts || _mapper->HasDefaultWorkRam()) {
|
|
return;
|
|
}
|
|
|
|
AddressInfo absAddr = _mapper->GetAbsoluteAddress(addr - 0x8000);
|
|
if(absAddr.Address >= 0) {
|
|
//Game Genie causes a bus conflict when the cartridge maps anything below $8000
|
|
//Only processed when addr >= $C020 because the mapper implementation never maps anything below $4020
|
|
value &= _mapper->DebugReadRam(addr - 0x8000);
|
|
}
|
|
}
|
|
}
|
|
|
|
void NesConsole::InitializeRam(void* data, uint32_t length)
|
|
{
|
|
EmuSettings* settings = _emu->GetSettings();
|
|
settings->InitializeRam(settings->GetNesConfig().RamPowerOnState, data, length);
|
|
}
|
|
|
|
DipSwitchInfo NesConsole::GetDipSwitchInfo()
|
|
{
|
|
DipSwitchInfo info = {};
|
|
info.DipSwitchCount = _mapper->GetMapperDipSwitchCount();
|
|
info.DatabaseId = _mapper->GetRomInfo().Hash.PrgCrc32;
|
|
return info;
|
|
}
|
|
|
|
void NesConsole::ProcessNotification(ConsoleNotificationType type, void* parameter)
|
|
{
|
|
if(type == ConsoleNotificationType::ExecuteShortcut) {
|
|
ExecuteShortcutParams* params = (ExecuteShortcutParams*)parameter;
|
|
switch(params->Shortcut) {
|
|
default: break;
|
|
case EmulatorShortcut::StartRecordHdPack: StartRecordingHdPack(*(HdPackBuilderOptions*)params->ParamPtr); break;
|
|
case EmulatorShortcut::StopRecordHdPack: StopRecordingHdPack(); break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void NesConsole::StartRecordingHdPack(HdPackBuilderOptions options)
|
|
{
|
|
auto lock = _emu->AcquireLock();
|
|
|
|
_emu->GetVideoDecoder()->WaitForAsyncFrameDecode();
|
|
|
|
std::stringstream saveState;
|
|
_emu->Serialize(saveState, false, 0);
|
|
|
|
_hdPackBuilder.reset();
|
|
_hdPackBuilder.reset(new HdPackBuilder(_emu, _ppu->GetPpuModel(), !_mapper->HasChrRom(), options));
|
|
|
|
_memoryManager->UnregisterIODevice(_ppu.get());
|
|
_ppu.reset(new HdBuilderPpu(this, _hdPackBuilder.get(), options.ChrRamBankSize));
|
|
_memoryManager->RegisterIODevice(_ppu.get());
|
|
|
|
_emu->Deserialize(saveState, SaveStateManager::FileFormatVersion, false);
|
|
_emu->GetSoundMixer()->StopAudio();
|
|
|
|
_emu->GetVideoDecoder()->ForceFilterUpdate();
|
|
}
|
|
|
|
void NesConsole::StopRecordingHdPack()
|
|
{
|
|
if(_hdPackBuilder) {
|
|
auto lock = _emu->AcquireLock();
|
|
|
|
_emu->GetVideoDecoder()->WaitForAsyncFrameDecode();
|
|
|
|
std::stringstream saveState;
|
|
_emu->Serialize(saveState, false, 0);
|
|
|
|
_memoryManager->UnregisterIODevice(_ppu.get());
|
|
if(_hdData) {
|
|
_ppu.reset(new HdNesPpu(this, _hdData.get()));
|
|
} else {
|
|
_ppu.reset(new DefaultNesPpu(this));
|
|
}
|
|
_memoryManager->RegisterIODevice(_ppu.get());
|
|
_hdPackBuilder.reset();
|
|
|
|
_emu->Deserialize(saveState, SaveStateManager::FileFormatVersion, false);
|
|
_emu->GetSoundMixer()->StopAudio();
|
|
_emu->GetVideoDecoder()->ForceFilterUpdate();
|
|
}
|
|
} |