mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
433 lines
No EOL
10 KiB
C++
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);
|
|
} |