Duckstation/src/core/system.cpp

437 lines
12 KiB
C++
Raw Normal View History

2019-09-09 07:01:26 +00:00
#include "system.h"
#include "YBaseLib/AutoReleasePtr.h"
2019-09-22 15:28:00 +00:00
#include "YBaseLib/Log.h"
2019-09-12 02:53:04 +00:00
#include "bus.h"
2019-09-17 14:22:41 +00:00
#include "cdrom.h"
2019-09-14 10:28:47 +00:00
#include "common/state_wrapper.h"
2019-09-12 02:53:04 +00:00
#include "cpu_core.h"
#include "dma.h"
#include "gpu.h"
#include "host_interface.h"
2019-09-17 06:26:00 +00:00
#include "interrupt_controller.h"
2019-09-29 02:51:34 +00:00
#include "mdec.h"
2019-10-27 06:45:23 +00:00
#include "memory_card.h"
#include "pad.h"
#include "pad_device.h"
#include "spu.h"
2019-09-20 13:40:19 +00:00
#include "timers.h"
2019-09-22 15:28:00 +00:00
#include <cstdio>
2019-10-12 12:15:38 +00:00
#include <imgui.h>
2019-09-22 15:28:00 +00:00
Log_SetChannel(System);
2019-09-09 07:01:26 +00:00
2019-11-11 08:19:57 +00:00
namespace BIOSHashes {
static constexpr char SCPH_1000[] = "239665b1a3dade1b5a52c06338011044";
static constexpr char SCPH_1001[] = "924e392ed05558ffdb115408c263dccf";
static constexpr char SCPH_1002[] = "54847e693405ffeb0359c6287434cbef";
static constexpr char SCPH_5500[] = "8dd7d5296a650fac7319bce665a6a53c";
static constexpr char SCPH_5501[] = "490f666e1afb15b7362b406ed1cea246";
static constexpr char SCPH_5502[] = "32736f17079d0b2b7024407c39bd3050";
} // namespace BIOSHashes
System::System(HostInterface* host_interface) : m_host_interface(host_interface)
2019-09-12 02:53:04 +00:00
{
m_cpu = std::make_unique<CPU::Core>();
m_bus = std::make_unique<Bus>();
m_dma = std::make_unique<DMA>();
2019-09-17 06:26:00 +00:00
m_interrupt_controller = std::make_unique<InterruptController>();
2019-09-17 09:22:39 +00:00
m_cdrom = std::make_unique<CDROM>();
m_pad = std::make_unique<Pad>();
2019-09-20 13:40:19 +00:00
m_timers = std::make_unique<Timers>();
m_spu = std::make_unique<SPU>();
2019-09-29 02:51:34 +00:00
m_mdec = std::make_unique<MDEC>();
2019-09-12 02:53:04 +00:00
}
2019-09-09 07:01:26 +00:00
System::~System() = default;
bool System::RecreateGPU()
{
// save current state
AutoReleasePtr<ByteStream> state_stream = ByteStream_CreateGrowableMemoryStream();
StateWrapper sw(state_stream, StateWrapper::Mode::Write);
const bool state_valid = m_gpu->DoState(sw);
if (!state_valid)
Log_ErrorPrintf("Failed to save old GPU state when switching renderers");
// create new renderer
m_gpu.reset();
if (!CreateGPU())
{
Panic("Failed to recreate GPU");
return false;
}
if (state_valid)
{
state_stream->SeekAbsolute(0);
sw.SetMode(StateWrapper::Mode::Read);
m_gpu->DoState(sw);
}
return true;
}
2019-09-09 07:01:26 +00:00
bool System::Initialize()
{
2019-11-11 08:19:57 +00:00
m_cpu->Initialize(m_bus.get());
m_bus->Initialize(m_cpu.get(), m_dma.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get(), m_pad.get(),
m_timers.get(), m_spu.get(), m_mdec.get());
2019-09-09 07:01:26 +00:00
2019-11-11 08:19:57 +00:00
m_dma->Initialize(this, m_bus.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get(), m_spu.get(),
m_mdec.get());
2019-09-09 07:01:26 +00:00
2019-11-11 08:19:57 +00:00
m_interrupt_controller->Initialize(m_cpu.get());
2019-11-11 08:19:57 +00:00
m_cdrom->Initialize(this, m_dma.get(), m_interrupt_controller.get(), m_spu.get());
m_pad->Initialize(this, m_interrupt_controller.get());
m_timers->Initialize(this, m_interrupt_controller.get());
m_spu->Initialize(this, m_dma.get(), m_interrupt_controller.get());
m_mdec->Initialize(this, m_dma.get());
2019-09-17 06:26:00 +00:00
if (!CreateGPU())
2019-09-09 07:01:26 +00:00
return false;
2019-11-11 08:19:57 +00:00
if (!LoadBIOS())
2019-09-29 02:51:34 +00:00
return false;
2019-10-27 06:45:23 +00:00
UpdateMemoryCards();
2019-09-09 07:01:26 +00:00
return true;
}
bool System::CreateGPU()
{
2019-11-07 15:07:39 +00:00
switch (m_host_interface->GetSettings().gpu_renderer)
{
case Settings::GPURenderer::HardwareOpenGL:
m_gpu = GPU::CreateHardwareOpenGLRenderer();
break;
2019-10-26 02:57:35 +00:00
#ifdef WIN32
case Settings::GPURenderer::HardwareD3D11:
m_gpu = GPU::CreateHardwareD3D11Renderer();
break;
#endif
case Settings::GPURenderer::Software:
2019-10-26 02:57:35 +00:00
default:
m_gpu = GPU::CreateSoftwareRenderer();
break;
}
if (!m_gpu || !m_gpu->Initialize(m_host_interface->GetDisplay(), this, m_dma.get(), m_interrupt_controller.get(),
m_timers.get()))
2019-10-26 02:57:35 +00:00
{
Log_ErrorPrintf("Failed to initialize GPU, falling back to software");
m_gpu.reset();
2019-11-07 15:07:39 +00:00
m_host_interface->GetSettings().gpu_renderer = Settings::GPURenderer::Software;
2019-10-26 02:57:35 +00:00
m_gpu = GPU::CreateSoftwareRenderer();
if (!m_gpu->Initialize(m_host_interface->GetDisplay(), this, m_dma.get(), m_interrupt_controller.get(),
m_timers.get()))
{
2019-10-26 02:57:35 +00:00
return false;
}
2019-10-26 02:57:35 +00:00
}
m_bus->SetGPU(m_gpu.get());
m_dma->SetGPU(m_gpu.get());
return true;
}
2019-11-11 08:19:57 +00:00
bool System::LoadBIOS()
{
if (!m_bus->LoadBIOS(GetSettings().bios_path.c_str()))
return false;
// apply patches
u8 bios_hash[16];
m_bus->GetBIOSHash(bios_hash);
SmallString bios_hash_string;
bios_hash_string.Format("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", bios_hash[0],
bios_hash[1], bios_hash[2], bios_hash[3], bios_hash[4], bios_hash[5], bios_hash[6],
bios_hash[7], bios_hash[8], bios_hash[9], bios_hash[10], bios_hash[11], bios_hash[12],
bios_hash[13], bios_hash[14], bios_hash[15]);
Log_InfoPrintf("BIOS hash: %s", bios_hash_string.GetCharArray());
if (bios_hash_string == BIOSHashes::SCPH_1000 || bios_hash_string == BIOSHashes::SCPH_1001 ||
bios_hash_string == BIOSHashes::SCPH_1002 || bios_hash_string == BIOSHashes::SCPH_5500 ||
bios_hash_string == BIOSHashes::SCPH_5501 || bios_hash_string == BIOSHashes::SCPH_5502)
{
// Patch to enable TTY.
Log_InfoPrintf("Patching BIOS to enable TTY/printf");
m_bus->PatchBIOS(0x1FC06F0C, 0x24010001);
m_bus->PatchBIOS(0x1FC06F14, 0xAF81A9C0);
}
else
{
Log_WarningPrintf("Unknown BIOS version, not patching TTY/printf");
}
return true;
}
2019-09-14 10:28:47 +00:00
bool System::DoState(StateWrapper& sw)
{
2019-10-04 10:48:29 +00:00
if (!sw.DoMarker("System"))
return false;
sw.Do(&m_frame_number);
sw.Do(&m_internal_frame_number);
sw.Do(&m_global_tick_counter);
2019-09-14 10:28:47 +00:00
if (!sw.DoMarker("CPU") || !m_cpu->DoState(sw))
return false;
if (!sw.DoMarker("Bus") || !m_bus->DoState(sw))
return false;
if (!sw.DoMarker("DMA") || !m_dma->DoState(sw))
return false;
2019-09-17 06:26:00 +00:00
if (!sw.DoMarker("InterruptController") || !m_interrupt_controller->DoState(sw))
return false;
2019-09-14 10:28:47 +00:00
if (!sw.DoMarker("GPU") || !m_gpu->DoState(sw))
return false;
2019-09-17 09:22:39 +00:00
if (!sw.DoMarker("CDROM") || !m_cdrom->DoState(sw))
return false;
if (!sw.DoMarker("Pad") || !m_pad->DoState(sw))
return false;
2019-09-20 13:40:19 +00:00
if (!sw.DoMarker("Timers") || !m_timers->DoState(sw))
return false;
2019-10-11 06:54:21 +00:00
if (!sw.DoMarker("SPU") || !m_spu->DoState(sw))
return false;
2019-09-29 02:51:34 +00:00
if (!sw.DoMarker("MDEC") || !m_mdec->DoState(sw))
return false;
2019-09-14 10:28:47 +00:00
return !sw.HasError();
}
2019-09-09 07:01:26 +00:00
void System::Reset()
{
2019-09-12 02:53:04 +00:00
m_cpu->Reset();
m_bus->Reset();
m_dma->Reset();
2019-09-17 06:26:00 +00:00
m_interrupt_controller->Reset();
2019-09-12 02:53:04 +00:00
m_gpu->Reset();
2019-09-17 09:22:39 +00:00
m_cdrom->Reset();
m_pad->Reset();
2019-09-20 13:40:19 +00:00
m_timers->Reset();
m_spu->Reset();
2019-09-29 02:51:34 +00:00
m_mdec->Reset();
m_frame_number = 1;
2019-10-04 10:48:29 +00:00
m_internal_frame_number = 0;
m_global_tick_counter = 0;
2019-09-09 07:01:26 +00:00
}
2019-09-14 10:28:47 +00:00
bool System::LoadState(ByteStream* state)
{
StateWrapper sw(state, StateWrapper::Mode::Read);
return DoState(sw);
}
bool System::SaveState(ByteStream* state)
{
StateWrapper sw(state, StateWrapper::Mode::Write);
return DoState(sw);
}
2019-09-09 07:01:26 +00:00
void System::RunFrame()
{
u32 current_frame_number = m_frame_number;
2019-09-17 04:25:25 +00:00
while (current_frame_number == m_frame_number)
{
m_cpu->Execute();
Synchronize();
2019-09-17 04:25:25 +00:00
}
2019-09-09 07:01:26 +00:00
}
bool System::LoadEXE(const char* filename)
{
#pragma pack(push, 1)
struct EXEHeader
{
char id[8]; // 0x000-0x007 PS-X EXE
char pad1[8]; // 0x008-0x00F
u32 initial_pc; // 0x010
u32 initial_gp; // 0x014
u32 load_address; // 0x018
u32 file_size; // 0x01C excluding 0x800-byte header
u32 unk0; // 0x020
u32 unk1; // 0x024
u32 memfill_start; // 0x028
u32 memfill_size; // 0x02C
u32 initial_sp_base; // 0x030
u32 initial_sp_offset; // 0x034
u32 reserved[5]; // 0x038-0x04B
char marker[0x7B4]; // 0x04C-0x7FF
};
static_assert(sizeof(EXEHeader) == 0x800);
#pragma pack(pop)
std::FILE* fp = std::fopen(filename, "rb");
if (!fp)
return false;
EXEHeader header;
if (std::fread(&header, sizeof(header), 1, fp) != 1)
{
std::fclose(fp);
return false;
}
if (header.memfill_size > 0)
{
const u32 words_to_write = header.memfill_size / 4;
u32 address = header.memfill_start & ~UINT32_C(3);
for (u32 i = 0; i < words_to_write; i++)
{
m_cpu->SafeWriteMemoryWord(address, 0);
address += sizeof(u32);
}
}
if (header.file_size >= 4)
{
std::vector<u32> data_words(header.file_size / 4);
if (std::fread(data_words.data(), header.file_size, 1, fp) != 1)
{
std::fclose(fp);
return false;
}
const u32 num_words = header.file_size / 4;
u32 address = header.load_address;
for (u32 i = 0; i < num_words; i++)
{
m_cpu->SafeWriteMemoryWord(address, data_words[i]);
address += sizeof(u32);
}
}
std::fclose(fp);
// patch the BIOS to jump to the executable directly
{
const u32 r_pc = header.load_address;
const u32 r_gp = header.initial_gp;
const u32 r_sp = header.initial_sp_base;
const u32 r_fp = header.initial_sp_base + header.initial_sp_offset;
// pc has to be done first because we can't load it in the delay slot
m_bus->PatchBIOS(0xBFC06FF0, UINT32_C(0x3C080000) | r_pc >> 16); // lui $t0, (r_pc >> 16)
m_bus->PatchBIOS(0xBFC06FF4, UINT32_C(0x35080000) | (r_pc & UINT32_C(0xFFFF))); // ori $t0, $t0, (r_pc & 0xFFFF)
m_bus->PatchBIOS(0xBFC06FF8, UINT32_C(0x3C1C0000) | r_gp >> 16); // lui $gp, (r_gp >> 16)
m_bus->PatchBIOS(0xBFC06FFC, UINT32_C(0x379C0000) | (r_gp & UINT32_C(0xFFFF))); // ori $gp, $gp, (r_gp & 0xFFFF)
m_bus->PatchBIOS(0xBFC07000, UINT32_C(0x3C1D0000) | r_sp >> 16); // lui $sp, (r_sp >> 16)
m_bus->PatchBIOS(0xBFC07004, UINT32_C(0x37BD0000) | (r_sp & UINT32_C(0xFFFF))); // ori $sp, $sp, (r_sp & 0xFFFF)
m_bus->PatchBIOS(0xBFC07008, UINT32_C(0x3C1E0000) | r_fp >> 16); // lui $fp, (r_fp >> 16)
m_bus->PatchBIOS(0xBFC0700C, UINT32_C(0x01000008)); // jr $t0
m_bus->PatchBIOS(0xBFC07010, UINT32_C(0x37DE0000) | (r_fp & UINT32_C(0xFFFF))); // ori $fp, $fp, (r_fp & 0xFFFF)
}
return true;
}
2019-09-14 10:28:47 +00:00
2019-09-22 15:28:00 +00:00
bool System::SetExpansionROM(const char* filename)
{
std::FILE* fp = std::fopen(filename, "rb");
if (!fp)
{
Log_ErrorPrintf("Failed to open '%s'", filename);
return false;
}
std::fseek(fp, 0, SEEK_END);
const u32 size = static_cast<u32>(std::ftell(fp));
std::fseek(fp, 0, SEEK_SET);
std::vector<u8> data(size);
if (std::fread(data.data(), size, 1, fp) != 1)
{
Log_ErrorPrintf("Failed to read ROM data from '%s'", filename);
std::fclose(fp);
return false;
}
std::fclose(fp);
Log_InfoPrintf("Loaded expansion ROM from '%s': %u bytes", filename, size);
m_bus->SetExpansionROM(std::move(data));
return true;
}
void System::Synchronize()
{
const TickCount pending_ticks = m_cpu->GetPendingTicks();
if (pending_ticks == 0)
return;
m_cpu->ResetPendingTicks();
2019-10-04 10:48:29 +00:00
m_cpu->ResetDowncount();
m_global_tick_counter += static_cast<u32>(pending_ticks);
m_gpu->Execute(pending_ticks);
2019-10-08 08:21:15 +00:00
m_timers->Execute(pending_ticks);
2019-09-21 15:12:16 +00:00
m_cdrom->Execute(pending_ticks);
2019-09-22 15:25:58 +00:00
m_pad->Execute(pending_ticks);
2019-10-10 16:20:21 +00:00
m_spu->Execute(pending_ticks);
m_mdec->Execute(pending_ticks);
m_dma->Execute(pending_ticks);
}
void System::SetDowncount(TickCount downcount)
2019-09-17 04:25:25 +00:00
{
m_cpu->SetDowncount(downcount);
2019-09-17 04:25:25 +00:00
}
void System::StallCPU(TickCount ticks)
{
m_cpu->AddPendingTicks(ticks);
}
2019-09-29 15:07:38 +00:00
void System::SetController(u32 slot, std::shared_ptr<PadDevice> dev)
{
2019-09-29 15:07:38 +00:00
m_pad->SetController(slot, std::move(dev));
}
2019-10-27 06:45:23 +00:00
void System::UpdateMemoryCards()
2019-09-29 15:07:38 +00:00
{
2019-10-27 06:45:23 +00:00
m_pad->SetMemoryCard(0, nullptr);
m_pad->SetMemoryCard(1, nullptr);
2019-11-07 15:07:39 +00:00
const Settings& settings = m_host_interface->GetSettings();
if (!settings.memory_card_a_path.empty())
2019-10-27 06:45:23 +00:00
{
2019-11-07 15:07:39 +00:00
std::shared_ptr<MemoryCard> card = MemoryCard::Open(this, settings.memory_card_a_path);
2019-10-27 06:45:23 +00:00
if (card)
m_pad->SetMemoryCard(0, std::move(card));
}
2019-11-07 15:07:39 +00:00
if (!settings.memory_card_b_path.empty())
2019-10-27 06:45:23 +00:00
{
2019-11-07 15:07:39 +00:00
std::shared_ptr<MemoryCard> card = MemoryCard::Open(this, settings.memory_card_b_path);
2019-10-27 06:45:23 +00:00
if (card)
m_pad->SetMemoryCard(1, std::move(card));
}
}
2019-09-20 10:14:00 +00:00
bool System::HasMedia() const
{
return m_cdrom->HasMedia();
}
bool System::InsertMedia(const char* path)
{
return m_cdrom->InsertMedia(path);
}
void System::RemoveMedia()
{
m_cdrom->RemoveMedia();
}