Mesen2/Core/PCE/PceConsole.cpp

433 lines
No EOL
10 KiB
C++

#include "pch.h"
#include "Shared/SettingTypes.h"
#include "PCE/PceConsole.h"
#include "PCE/PceControlManager.h"
#include "PCE/PceMemoryManager.h"
#include "PCE/PceDefaultVideoFilter.h"
#include "PCE/PceNtscFilter.h"
#include "PCE/PceCpu.h"
#include "PCE/PceVdc.h"
#include "PCE/PceVce.h"
#include "PCE/PceVpc.h"
#include "PCE/PcePsg.h"
#include "PCE/PceTimer.h"
#include "PCE/PceConstants.h"
#include "PCE/IPceMapper.h"
#include "PCE/CdRom/PceCdRom.h"
#include "PCE/CdRom/PceArcadeCard.h"
#include "PCE/PceSf2RomMapper.h"
#include "PCE/HesFileData.h"
#include "Utilities/Serializer.h"
#include "Utilities/CRC32.h"
#include "Shared/MemoryType.h"
#include "Shared/FirmwareHelper.h"
PceConsole::PceConsole(Emulator* emu)
{
_emu = emu;
}
PceConsole::~PceConsole()
{
}
void PceConsole::Reset()
{
//The PC Engine has no reset button, behave like power cycle
_emu->ReloadRom(true);
}
LoadRomResult PceConsole::LoadRom(VirtualFile& romFile)
{
PcEngineConfig& cfg = _emu->GetSettings()->GetPcEngineConfig();
PceConsoleType consoleType = cfg.ConsoleType;
vector<uint8_t> romData;
uint32_t crc32 = 0;
bool cdromUnitEnabled = false;
if(romFile.CheckFileSignature({ "HESM" })) {
_romFormat = RomFormat::PceHes;
if(!LoadHesFile(romFile)) {
return LoadRomResult::Failure;
}
romData = _hesData->RomData;
} else if(romFile.GetFileExtension() == ".cue") {
DiscInfo disc = {};
if(!CdReader::LoadCue(romFile, disc)) {
return LoadRomResult::Failure;
}
if(!LoadFirmware(disc, romData)) {
return LoadRomResult::Failure;
}
_cdrom.reset(new PceCdRom(_emu, this, disc));
_romFormat = RomFormat::PceCdRom;
cdromUnitEnabled = true;
} else {
romFile.ReadFile(romData);
crc32 = CRC32::GetCRC(romData);
if((romData.size() % 0x2000) == 512) {
//File probably has header, discard it
romData.erase(romData.begin(), romData.begin() + 512);
}
if(consoleType == PceConsoleType::Auto && (romFile.GetFileExtension() == ".sgx" || IsSuperGrafxCard(crc32))) {
consoleType = PceConsoleType::SuperGrafx;
}
cdromUnitEnabled = cfg.EnableCdRomForHuCardGames;
if(cdromUnitEnabled || !cfg.DisableCdRomSaveRamForHuCardGames) {
//CD-ROM is used for save ram for non-cd-rom games
DiscInfo emptyDisc = {};
_cdrom.reset(new PceCdRom(_emu, this, emptyDisc));
}
_romFormat = RomFormat::Pce;
}
uint32_t cardRamSize = 0;
if(_romFormat == RomFormat::PceCdRom) {
if(cfg.CdRomType == PceCdRomType::Arcade) {
_mapper.reset(new PceArcadeCard(this, _emu));
}
if(cfg.CdRomType == PceCdRomType::SuperCdRom || cfg.CdRomType == PceCdRomType::Arcade) {
cardRamSize = 0x30000;
}
} else if(romData.size() > 0x80 * 0x2000) {
//For ROMs over 1MB, assume this is the SF2 board
_mapper.reset(new PceSf2RomMapper(this));
} else if(IsPopulousCard(crc32)) {
cardRamSize = 0x8000;
}
_controlManager.reset(new PceControlManager(_emu));
_vce.reset(new PceVce(_emu, this));
_vpc.reset(new PceVpc(_emu, this, _vce.get()));
_vdc.reset(new PceVdc(_emu, this, _vpc.get(), _vce.get(), false));
if(consoleType == PceConsoleType::SuperGrafx) {
_vdc2.reset(new PceVdc(_emu, this, _vpc.get(), _vce.get(), true));
}
_vpc->ConnectVdc(_vdc.get(), _vdc2.get());
_timer.reset(new PceTimer(this));
_psg.reset(new PcePsg(_emu, this));
_memoryManager.reset(new PceMemoryManager(_emu, this, _vpc.get(), _vce.get(), _controlManager.get(), _psg.get(), _timer.get(), _mapper.get(), _cdrom.get(), romData, cardRamSize, cdromUnitEnabled));
_cpu.reset(new PceCpu(_emu, _memoryManager.get()));
if(_hesData) {
InitHesPlayback(_hesData->CurrentTrack);
}
MessageManager::Log("-----------------");
MessageManager::Log("Loaded: " + romFile.GetFileName());
MessageManager::Log("-----------------");
return LoadRomResult::Success;
}
bool PceConsole::LoadFirmware(DiscInfo& disc, vector<uint8_t>& romData)
{
if(disc.Tracks[0].Format != TrackFormat::Audio) {
//All Games Express discs have these 8 bytes at the start of sector 0x10 (which is the first sector read by the firmware)
constexpr uint8_t signature[8] { 0x01, 0x43, 0x44, 0x30, 0x30, 0x31, 0x01, 0x00 };
vector<uint8_t> data;
disc.ReadDataSector(0x10, data);
if(memcmp(data.data(), signature, sizeof(signature)) == 0) {
return FirmwareHelper::LoadPceGamesExpressFirmware(_emu, romData);
}
}
return FirmwareHelper::LoadPceSuperCdFirmware(_emu, romData);
}
bool PceConsole::IsPopulousCard(uint32_t crc32)
{
return crc32 == 0x083C956A;
}
bool PceConsole::IsSuperGrafxCard(uint32_t crc32)
{
//These are the 5 SuperGrafx-exclusive games
return crc32 == 0xB486A8ED || crc32 == 0x1F041166 || crc32 == 0x3B13AF61 || crc32 == 0x4C2126B0 || crc32 == 0x8C4588E2;
}
void PceConsole::RunFrame()
{
uint32_t frameCount = _vdc->GetFrameCount();
while(frameCount == _vdc->GetFrameCount()) {
_cpu->Exec();
}
_psg->Run();
_psg->PlayQueuedAudio();
}
void PceConsole::ProcessEndOfFrame()
{
//Run the PSG at least once per frame to prevent issues when a very
//large block transfer (TIA, etc.) is running across multiple frames
//(RunFrame above can run more than one frame in this scenario, which can cause issues)
_psg->Run();
_psg->PlayQueuedAudio();
}
void PceConsole::SaveBattery()
{
if(_cdrom) {
_cdrom->SaveBattery();
}
}
BaseControlManager* PceConsole::GetControlManager()
{
return _controlManager.get();
}
ConsoleRegion PceConsole::GetRegion()
{
return ConsoleRegion::Ntsc;
}
ConsoleType PceConsole::GetConsoleType()
{
return ConsoleType::PcEngine;
}
vector<CpuType> PceConsole::GetCpuTypes()
{
return { CpuType::Pce };
}
PceCpu* PceConsole::GetCpu()
{
return _cpu.get();
}
PceVdc* PceConsole::GetVdc()
{
return _vdc.get();
}
PceVce* PceConsole::GetVce()
{
return _vce.get();
}
PceVpc* PceConsole::GetVpc()
{
return _vpc.get();
}
PcePsg* PceConsole::GetPsg()
{
return _psg.get();
}
PceMemoryManager* PceConsole::GetMemoryManager()
{
return _memoryManager.get();
}
uint64_t PceConsole::GetMasterClock()
{
return _memoryManager->GetState().CycleCount;
}
uint32_t PceConsole::GetMasterClockRate()
{
return PceConstants::MasterClockRate;
}
double PceConsole::GetFps()
{
//Varies depending on VCE setting (263 or 262 scanlines)
return (double)PceConstants::MasterClockRate / PceConstants::ClockPerScanline / _vce->GetState().ScanlineCount;
}
BaseVideoFilter* PceConsole::GetVideoFilter(bool getDefaultFilter)
{
if(getDefaultFilter || _romFormat == RomFormat::PceHes) {
return new PceDefaultVideoFilter(_emu);
}
VideoFilterType filterType = _emu->GetSettings()->GetVideoConfig().VideoFilter;
switch(filterType) {
case VideoFilterType::NtscBlargg:
case VideoFilterType::NtscBisqwit:
return new PceNtscFilter(_emu);
default:
return new PceDefaultVideoFilter(_emu);
}
}
PpuFrameInfo PceConsole::GetPpuFrame()
{
PpuFrameInfo frame = {};
PceVdcState& state = _vdc->GetState();
frame.FrameCount = state.FrameCount;
frame.CycleCount = PceConstants::ClockPerScanline;
frame.FirstScanline = 0;
frame.ScanlineCount = PceConstants::ScanlineCount;
frame.FrameBuffer = (uint8_t*)_vpc->GetScreenBuffer();
//TODO - height/width/scanlinecount vary based on VDC settings
frame.Height = PceConstants::InternalOutputHeight;
frame.Width = PceConstants::InternalOutputWidth;
frame.FrameBufferSize = PceConstants::MaxScreenWidth * (PceConstants::ScreenHeight + 1) * sizeof(uint16_t);
return frame;
}
RomFormat PceConsole::GetRomFormat()
{
return _romFormat;
}
bool PceConsole::LoadHesFile(VirtualFile& hesFile)
{
unique_ptr<HesFileData> hesData(new HesFileData());
if(!hesData->LoadFile(hesFile)) {
return false;
}
_hesData.swap(hesData);
return true;
}
void PceConsole::InitHesPlayback(uint8_t selectedTrack)
{
_hesData->CurrentTrack = selectedTrack;
_cpu->GetState().A = _hesData->CurrentTrack;
_cpu->GetState().PC = _hesData->RequestAddress;
for(int i = 0; i < 8; i++) {
_memoryManager->SetMprValue(1 << i, _hesData->InitialMpr[i]);
}
//Force all RAM to 0, otherwise most HES files don't play properly
for(int i = 0; i < 0x2000; i++) {
_memoryManager->DebugWrite(0x2000 | i, 0);
}
//Setup stack to return to 0x1C00 on RTS, 0x1C00 is setup to cause an infinite loop with BRA
_cpu->GetState().SP = 0xFD;
_memoryManager->DebugWrite(0x21FE, 0xFF);
_memoryManager->DebugWrite(0x21FF, 0x1B);
}
AudioTrackInfo PceConsole::GetAudioTrackInfo()
{
return _hesData ? _hesData->GetAudioTrackInfo((double)GetMasterClock() / GetMasterClockRate()) : AudioTrackInfo {};
}
void PceConsole::ProcessAudioPlayerAction(AudioPlayerActionParams p)
{
if(_hesData) {
_hesData->ProcessAudioPlayerAction(p, _emu);
}
}
AddressInfo PceConsole::GetAbsoluteAddress(AddressInfo& relAddress)
{
if(relAddress.Type == MemoryType::PceMemory) {
return _memoryManager->GetAbsoluteAddress(relAddress.Address);
}
return { -1, MemoryType::None };
}
AddressInfo PceConsole::GetRelativeAddress(AddressInfo& absAddress, CpuType cpuType)
{
return _memoryManager->GetRelativeAddress(absAddress, _cpu->GetState().PC);
}
PceVideoState PceConsole::GetVideoState()
{
PceVideoState state;
state.Vdc = _vdc->GetState();
state.Vce = _vce->GetState();
state.Vpc = _vpc->GetState();
if(_vdc2) {
state.Vdc2 = _vdc2->GetState();
} else {
state.Vdc2 = {};
}
return state;
}
void PceConsole::SetVideoState(PceVideoState& state)
{
_vdc->GetState() = state.Vdc;
_vce->GetState() = state.Vce;
_vpc->GetState() = state.Vpc;
if(_vdc2) {
_vdc2->GetState() = state.Vdc2;
}
}
void PceConsole::GetConsoleState(BaseState& baseState, ConsoleType consoleType)
{
PceState& state = (PceState&)baseState;
state.Cpu = _cpu->GetState();
state.Video = GetVideoState();
state.MemoryManager = _memoryManager->GetState();
state.Timer = _timer->GetState();
state.Psg = _psg->GetState();
for(int i = 0; i < 6; i++) {
state.PsgChannels[i] = _psg->GetChannelState(i);
}
state.HasArcadeCard = false;
if(_mapper && dynamic_cast<PceArcadeCard*>(_mapper.get())) {
state.ArcadeCard = ((PceArcadeCard*)_mapper.get())->GetState();
state.HasArcadeCard = true;
}
if(_cdrom) {
state.CdRom = _cdrom->GetState();
state.Adpcm = _cdrom->GetAdpcmState();
state.AudioFader = _cdrom->GetAudioFaderState();
state.CdPlayer = _cdrom->GetCdPlayerState();
state.ScsiDrive = _cdrom->GetScsiState();
}
state.IsSuperGrafx = _vdc2 != nullptr;
state.HasCdRom = _cdrom != nullptr;
}
void PceConsole::Serialize(Serializer& s)
{
SV(_cpu);
SV(_vdc);
if(_vdc2) {
SV(_vdc2);
}
SV(_vpc);
SV(_vce);
SV(_psg);
SV(_memoryManager);
SV(_timer);
if(_cdrom) {
SV(_cdrom);
}
if(_mapper) {
SV(_mapper);
}
SV(_controlManager);
}
void PceConsole::InitializeRam(void* data, uint32_t length)
{
EmuSettings* settings = _emu->GetSettings();
settings->InitializeRam(settings->GetPcEngineConfig().RamPowerOnState, data, length);
}