mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-18 06:25:37 +00:00
System: Scaffolding for multi-system/multi-bios
This commit is contained in:
parent
d6209937fb
commit
246c97ccb3
|
@ -1,4 +1,6 @@
|
|||
add_library(core
|
||||
bios.cpp
|
||||
bios.h
|
||||
bus.cpp
|
||||
bus.h
|
||||
bus.inl
|
||||
|
|
178
src/core/bios.cpp
Normal file
178
src/core/bios.cpp
Normal file
|
@ -0,0 +1,178 @@
|
|||
#include "bios.h"
|
||||
#include "YBaseLib/Log.h"
|
||||
#include "YBaseLib/MD5Digest.h"
|
||||
#include "cpu_disasm.h"
|
||||
Log_SetChannel(BIOS);
|
||||
|
||||
namespace BIOS {
|
||||
static constexpr Hash MakeHashFromString(const char str[])
|
||||
{
|
||||
Hash h{};
|
||||
for (int i = 0; str[i] != '\0'; i++)
|
||||
{
|
||||
u8 nibble = 0;
|
||||
char ch = str[i];
|
||||
if (ch >= '0' && ch <= '9')
|
||||
nibble = str[i] - '0';
|
||||
else if (ch >= 'a' && ch <= 'z')
|
||||
nibble = 0xA + (str[i] - 'a');
|
||||
else if (ch >= 'A' && ch <= 'Z')
|
||||
nibble = 0xA + (str[i] - 'A');
|
||||
|
||||
h.bytes[i / 2] |= nibble << (((i & 1) ^ 1) * 4);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
std::string Hash::ToString() const
|
||||
{
|
||||
char str[33];
|
||||
std::snprintf(str, sizeof(str), "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", bytes[0],
|
||||
bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10],
|
||||
bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]);
|
||||
return str;
|
||||
}
|
||||
|
||||
static constexpr Hash SCPH_1000_HASH = MakeHashFromString("239665b1a3dade1b5a52c06338011044");
|
||||
static constexpr Hash SCPH_1001_HASH = MakeHashFromString("924e392ed05558ffdb115408c263dccf");
|
||||
static constexpr Hash SCPH_1002_HASH = MakeHashFromString("54847e693405ffeb0359c6287434cbef");
|
||||
static constexpr Hash SCPH_5500_HASH = MakeHashFromString("8dd7d5296a650fac7319bce665a6a53c");
|
||||
static constexpr Hash SCPH_5501_HASH = MakeHashFromString("490f666e1afb15b7362b406ed1cea246");
|
||||
static constexpr Hash SCPH_5502_HASH = MakeHashFromString("32736f17079d0b2b7024407c39bd3050");
|
||||
|
||||
Hash GetHash(const Image& image)
|
||||
{
|
||||
Hash hash;
|
||||
MD5Digest digest;
|
||||
digest.Update(image.data(), static_cast<u32>(image.size()));
|
||||
digest.Final(hash.bytes);
|
||||
return hash;
|
||||
}
|
||||
|
||||
std::optional<Image> LoadImageFromFile(std::string_view filename)
|
||||
{
|
||||
Image ret(BIOS_SIZE);
|
||||
std::string filename_str(filename);
|
||||
std::FILE* fp = std::fopen(filename_str.c_str(), "rb");
|
||||
if (!fp)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to open BIOS image '%s'", filename_str.c_str());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::fseek(fp, 0, SEEK_END);
|
||||
const u32 size = static_cast<u32>(std::ftell(fp));
|
||||
std::fseek(fp, 0, SEEK_SET);
|
||||
|
||||
if (size != BIOS_SIZE)
|
||||
{
|
||||
Log_ErrorPrintf("BIOS image '%s' mismatch, expecting %u bytes, got %u bytes", filename_str.c_str(), BIOS_SIZE,
|
||||
size);
|
||||
std::fclose(fp);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (std::fread(ret.data(), 1, ret.size(), fp) != ret.size())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to read BIOS image '%s'", filename_str.c_str());
|
||||
std::fclose(fp);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<Hash> GetHashForFile(const std::string_view filename)
|
||||
{
|
||||
auto image = LoadImageFromFile(filename);
|
||||
if (!image)
|
||||
return std::nullopt;
|
||||
|
||||
return GetHash(*image);
|
||||
}
|
||||
|
||||
bool IsValidHashForRegion(ConsoleRegion region, const Hash& hash)
|
||||
{
|
||||
switch (region)
|
||||
{
|
||||
case ConsoleRegion::NTSC_J:
|
||||
return (hash == SCPH_1000_HASH || hash == SCPH_5500_HASH);
|
||||
|
||||
case ConsoleRegion::NTSC_U:
|
||||
return (hash == SCPH_1001_HASH || hash == SCPH_5501_HASH);
|
||||
|
||||
case ConsoleRegion::PAL:
|
||||
return (hash == SCPH_1002_HASH || hash == SCPH_5502_HASH);
|
||||
|
||||
case ConsoleRegion::Auto:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void PatchBIOS(Image& bios, u32 address, u32 value, u32 mask /*= UINT32_C(0xFFFFFFFF)*/)
|
||||
{
|
||||
const u32 phys_address = address & UINT32_C(0x1FFFFFFF);
|
||||
const u32 offset = phys_address - BIOS_BASE;
|
||||
Assert(phys_address >= BIOS_BASE && offset < BIOS_SIZE);
|
||||
|
||||
u32 existing_value;
|
||||
std::memcpy(&existing_value, &bios[offset], sizeof(existing_value));
|
||||
u32 new_value = (existing_value & ~mask) | value;
|
||||
std::memcpy(&bios[offset], &new_value, sizeof(new_value));
|
||||
|
||||
SmallString old_disasm, new_disasm;
|
||||
CPU::DisassembleInstruction(&old_disasm, address, existing_value);
|
||||
CPU::DisassembleInstruction(&new_disasm, address, new_value);
|
||||
Log_DevPrintf("BIOS-Patch 0x%08X (+0x%X): 0x%08X %s -> %08X %s", address, offset, existing_value,
|
||||
old_disasm.GetCharArray(), new_value, new_disasm.GetCharArray());
|
||||
}
|
||||
|
||||
bool PatchBIOSEnableTTY(Image& image, const Hash& hash)
|
||||
{
|
||||
if (hash != SCPH_1000_HASH && hash != SCPH_1001_HASH && hash != SCPH_1002_HASH && hash != SCPH_5500_HASH &&
|
||||
hash != SCPH_5501_HASH && hash != SCPH_5502_HASH)
|
||||
{
|
||||
Log_WarningPrintf("Incompatible version for TTY patch: %s", hash.ToString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
Log_InfoPrintf("Patching BIOS to enable TTY/printf");
|
||||
PatchBIOS(image, 0x1FC06F0C, 0x24010001);
|
||||
PatchBIOS(image, 0x1FC06F14, 0xAF81A9C0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PatchBIOSFastBoot(Image& image, const Hash& hash)
|
||||
{
|
||||
if (hash != SCPH_1000_HASH && hash != SCPH_1001_HASH && hash != SCPH_1002_HASH && hash != SCPH_5500_HASH &&
|
||||
hash != SCPH_5501_HASH && hash != SCPH_5502_HASH)
|
||||
{
|
||||
Log_WarningPrintf("Incompatible version for fast-boot patch: %s", hash.ToString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Replace the shell entry point with a return back to the bootstrap.
|
||||
Log_InfoPrintf("Patching BIOS to skip intro");
|
||||
PatchBIOS(image, 0x1FC18000, 0x03E00008);
|
||||
PatchBIOS(image, 0x1FC18004, 0x00000000);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PatchBIOSForEXE(Image& image, u32 r_pc, u32 r_gp, u32 r_sp, u32 r_fp)
|
||||
{
|
||||
// pc has to be done first because we can't load it in the delay slot
|
||||
PatchBIOS(image, 0xBFC06FF0, UINT32_C(0x3C080000) | r_pc >> 16); // lui $t0, (r_pc >> 16)
|
||||
PatchBIOS(image, 0xBFC06FF4, UINT32_C(0x35080000) | (r_pc & UINT32_C(0xFFFF))); // ori $t0, $t0, (r_pc & 0xFFFF)
|
||||
PatchBIOS(image, 0xBFC06FF8, UINT32_C(0x3C1C0000) | r_gp >> 16); // lui $gp, (r_gp >> 16)
|
||||
PatchBIOS(image, 0xBFC06FFC, UINT32_C(0x379C0000) | (r_gp & UINT32_C(0xFFFF))); // ori $gp, $gp, (r_gp & 0xFFFF)
|
||||
PatchBIOS(image, 0xBFC07000, UINT32_C(0x3C1D0000) | r_sp >> 16); // lui $sp, (r_sp >> 16)
|
||||
PatchBIOS(image, 0xBFC07004, UINT32_C(0x37BD0000) | (r_sp & UINT32_C(0xFFFF))); // ori $sp, $sp, (r_sp & 0xFFFF)
|
||||
PatchBIOS(image, 0xBFC07008, UINT32_C(0x3C1E0000) | r_fp >> 16); // lui $fp, (r_fp >> 16)
|
||||
PatchBIOS(image, 0xBFC0700C, UINT32_C(0x01000008)); // jr $t0
|
||||
PatchBIOS(image, 0xBFC07010, UINT32_C(0x37DE0000) | (r_fp & UINT32_C(0xFFFF))); // ori $fp, $fp, (r_fp & 0xFFFF)
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace BIOS
|
37
src/core/bios.h
Normal file
37
src/core/bios.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
#include "types.h"
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace BIOS {
|
||||
enum : u32
|
||||
{
|
||||
BIOS_BASE = 0x1FC00000,
|
||||
BIOS_SIZE = 0x80000
|
||||
};
|
||||
|
||||
using Image = std::vector<u8>;
|
||||
|
||||
struct Hash
|
||||
{
|
||||
u8 bytes[16];
|
||||
|
||||
ALWAYS_INLINE bool operator==(const Hash& bh) const { return (std::memcmp(bytes, bh.bytes, sizeof(bytes)) == 0); }
|
||||
ALWAYS_INLINE bool operator!=(const Hash& bh) const { return (std::memcmp(bytes, bh.bytes, sizeof(bytes)) != 0); }
|
||||
|
||||
std::string ToString() const;
|
||||
};
|
||||
|
||||
Hash GetHash(const Image& image);
|
||||
std::optional<Image> LoadImageFromFile(std::string_view filename);
|
||||
std::optional<Hash> GetHashForFile(std::string_view filename);
|
||||
|
||||
bool IsValidHashForRegion(ConsoleRegion region, const Hash& hash);
|
||||
|
||||
void PatchBIOS(Image& image, u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF));
|
||||
|
||||
bool PatchBIOSEnableTTY(Image& image, const Hash& hash);
|
||||
bool PatchBIOSFastBoot(Image& image, const Hash& hash);
|
||||
bool PatchBIOSForEXE(Image& image, u32 r_pc, u32 r_gp, u32 r_sp, u32 r_fp);
|
||||
} // namespace BIOS
|
|
@ -166,65 +166,20 @@ TickCount Bus::WriteWords(PhysicalMemoryAddress address, const u32* words, u32 w
|
|||
return static_cast<TickCount>(word_count + ((word_count + 15) / 16));
|
||||
}
|
||||
|
||||
void Bus::PatchBIOS(u32 address, u32 value, u32 mask /*= UINT32_C(0xFFFFFFFF)*/)
|
||||
{
|
||||
const u32 phys_address = address & UINT32_C(0x1FFFFFFF);
|
||||
const u32 offset = phys_address - BIOS_BASE;
|
||||
Assert(phys_address >= BIOS_BASE && offset < BIOS_SIZE);
|
||||
|
||||
u32 existing_value;
|
||||
std::memcpy(&existing_value, &m_bios[offset], sizeof(existing_value));
|
||||
u32 new_value = (existing_value & ~mask) | value;
|
||||
std::memcpy(&m_bios[offset], &new_value, sizeof(new_value));
|
||||
|
||||
SmallString old_disasm, new_disasm;
|
||||
CPU::DisassembleInstruction(&old_disasm, address, existing_value);
|
||||
CPU::DisassembleInstruction(&new_disasm, address, new_value);
|
||||
Log_InfoPrintf("BIOS-Patch 0x%08X (+0x%X): 0x%08X %s -> %08X %s", address, offset, existing_value,
|
||||
old_disasm.GetCharArray(), new_value, new_disasm.GetCharArray());
|
||||
}
|
||||
|
||||
void Bus::GetBIOSHash(u8 hash[16])
|
||||
{
|
||||
MD5Digest digest;
|
||||
digest.Update(m_bios.data(), static_cast<u32>(m_bios.size()));
|
||||
digest.Final(hash);
|
||||
}
|
||||
|
||||
void Bus::SetExpansionROM(std::vector<u8> data)
|
||||
{
|
||||
m_exp1_rom = std::move(data);
|
||||
}
|
||||
|
||||
bool Bus::LoadBIOS(const char* filename)
|
||||
void Bus::SetBIOS(const std::vector<u8>& image)
|
||||
{
|
||||
std::FILE* fp = std::fopen(filename, "rb");
|
||||
if (!fp)
|
||||
if (image.size() != static_cast<u32>(BIOS_SIZE))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to open BIOS image '%s'", filename);
|
||||
return false;
|
||||
Panic("Incorrect BIOS image size");
|
||||
return;
|
||||
}
|
||||
|
||||
std::fseek(fp, 0, SEEK_END);
|
||||
const u32 size = static_cast<u32>(std::ftell(fp));
|
||||
std::fseek(fp, 0, SEEK_SET);
|
||||
|
||||
if (size != m_bios.size())
|
||||
{
|
||||
Log_ErrorPrintf("BIOS image mismatch, expecting %u bytes, got %u bytes", static_cast<u32>(m_bios.size()), size);
|
||||
std::fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::fread(m_bios.data(), 1, m_bios.size(), fp) != m_bios.size())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to read BIOS image");
|
||||
std::fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::fclose(fp);
|
||||
return true;
|
||||
std::copy(image.cbegin(), image.cend(), m_bios.begin());
|
||||
}
|
||||
|
||||
std::tuple<TickCount, TickCount, TickCount> Bus::CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay)
|
||||
|
|
|
@ -32,10 +32,6 @@ public:
|
|||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
bool LoadBIOS(const char* filename);
|
||||
void PatchBIOS(u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF));
|
||||
void GetBIOSHash(u8 hash[16]);
|
||||
|
||||
bool ReadByte(PhysicalMemoryAddress address, u8* value);
|
||||
bool ReadHalfWord(PhysicalMemoryAddress address, u16* value);
|
||||
bool ReadWord(PhysicalMemoryAddress address, u32* value);
|
||||
|
@ -51,6 +47,7 @@ public:
|
|||
TickCount WriteWords(PhysicalMemoryAddress address, const u32* words, u32 word_count);
|
||||
|
||||
void SetExpansionROM(std::vector<u8> data);
|
||||
void SetBIOS(const std::vector<u8>& image);
|
||||
|
||||
// changing interfaces
|
||||
void SetGPU(GPU* gpu) { m_gpu = gpu; }
|
||||
|
|
|
@ -147,20 +147,12 @@ bool CDROM::DoState(StateWrapper& sw)
|
|||
return !sw.HasError();
|
||||
}
|
||||
|
||||
bool CDROM::InsertMedia(const char* filename)
|
||||
void CDROM::InsertMedia(std::unique_ptr<CDImage> media)
|
||||
{
|
||||
auto media = CDImage::Open(filename);
|
||||
if (!media)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to open media at '%s'", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HasMedia())
|
||||
RemoveMedia();
|
||||
|
||||
m_media = std::move(media);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDROM::RemoveMedia()
|
||||
|
|
|
@ -27,7 +27,7 @@ public:
|
|||
bool DoState(StateWrapper& sw);
|
||||
|
||||
bool HasMedia() const { return static_cast<bool>(m_media); }
|
||||
bool InsertMedia(const char* filename);
|
||||
void InsertMedia(std::unique_ptr<CDImage> media);
|
||||
void RemoveMedia();
|
||||
|
||||
// I/O
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="bios.cpp" />
|
||||
<ClCompile Include="bus.cpp" />
|
||||
<ClCompile Include="cdrom.cpp" />
|
||||
<ClCompile Include="cpu_core.cpp" />
|
||||
|
@ -61,6 +62,7 @@
|
|||
<ClCompile Include="timers.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="bios.h" />
|
||||
<ClInclude Include="bus.h" />
|
||||
<ClInclude Include="cdrom.h" />
|
||||
<ClInclude Include="cpu_core.h" />
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
<ClCompile Include="gpu_sw.cpp" />
|
||||
<ClCompile Include="gpu_hw_shadergen.cpp" />
|
||||
<ClCompile Include="gpu_hw_d3d11.cpp" />
|
||||
<ClCompile Include="bios.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="types.h" />
|
||||
|
@ -55,6 +56,7 @@
|
|||
<ClInclude Include="gpu_hw_shadergen.h" />
|
||||
<ClInclude Include="gpu_hw_d3d11.h" />
|
||||
<ClInclude Include="host_display.h" />
|
||||
<ClInclude Include="bios.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="cpu_core.inl" />
|
||||
|
|
|
@ -21,6 +21,15 @@ class Timers;
|
|||
class GPU
|
||||
{
|
||||
public:
|
||||
enum class State : u8
|
||||
{
|
||||
Idle,
|
||||
WaitingForParameters,
|
||||
ExecutingCommand,
|
||||
ReadingVRAM,
|
||||
WritingVRAM
|
||||
};
|
||||
|
||||
enum class DMADirection : u32
|
||||
{
|
||||
Off = 0,
|
||||
|
@ -72,15 +81,6 @@ public:
|
|||
Disabled = 4 // Not a register value
|
||||
};
|
||||
|
||||
enum class State : u8
|
||||
{
|
||||
Idle,
|
||||
WaitingForParameters,
|
||||
ExecutingCommand,
|
||||
ReadingVRAM,
|
||||
WritingVRAM
|
||||
};
|
||||
|
||||
enum : u32
|
||||
{
|
||||
VRAM_WIDTH = 1024,
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#include "host_interface.h"
|
||||
#include "YBaseLib/ByteStream.h"
|
||||
#include "YBaseLib/Log.h"
|
||||
#include "bios.h"
|
||||
#include "common/audio_stream.h"
|
||||
#include "host_display.h"
|
||||
#include "system.h"
|
||||
#include <filesystem>
|
||||
Log_SetChannel(HostInterface);
|
||||
|
||||
HostInterface::HostInterface()
|
||||
|
@ -13,67 +15,113 @@ HostInterface::HostInterface()
|
|||
|
||||
HostInterface::~HostInterface() = default;
|
||||
|
||||
bool HostInterface::InitializeSystem(const char* filename, const char* exp1_filename)
|
||||
bool HostInterface::CreateSystem()
|
||||
{
|
||||
m_system = std::make_unique<System>(this);
|
||||
if (!m_system->Initialize())
|
||||
{
|
||||
m_system.reset();
|
||||
return false;
|
||||
}
|
||||
m_system = System::Create(this);
|
||||
|
||||
m_system->Reset();
|
||||
|
||||
if (filename)
|
||||
{
|
||||
const StaticString filename_str(filename);
|
||||
if (filename_str.EndsWith(".psexe", false) || filename_str.EndsWith(".exe", false))
|
||||
{
|
||||
Log_InfoPrintf("Sideloading EXE file '%s'", filename);
|
||||
if (!m_system->LoadEXE(filename))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to load EXE file '%s'", filename);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_InfoPrintf("Inserting CDROM from image file '%s'", filename);
|
||||
if (!m_system->InsertMedia(filename))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to insert media '%s'", filename);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exp1_filename)
|
||||
m_system->SetExpansionROM(exp1_filename);
|
||||
|
||||
// Resume execution.
|
||||
// Pull in any invalid settings which have been reset.
|
||||
m_settings = m_system->GetSettings();
|
||||
m_paused = true;
|
||||
UpdateAudioVisualSync();
|
||||
return true;
|
||||
}
|
||||
|
||||
void HostInterface::ShutdownSystem()
|
||||
bool HostInterface::BootSystem(const char* filename, const char* state_filename)
|
||||
{
|
||||
if (!m_system->Boot(filename))
|
||||
return false;
|
||||
|
||||
m_paused = m_settings.start_paused;
|
||||
ConnectControllers();
|
||||
UpdateAudioVisualSync();
|
||||
return true;
|
||||
}
|
||||
|
||||
void HostInterface::DestroySystem()
|
||||
{
|
||||
m_system.reset();
|
||||
m_paused = false;
|
||||
UpdateAudioVisualSync();
|
||||
}
|
||||
|
||||
void HostInterface::ReportError(const char* message)
|
||||
{
|
||||
Log_ErrorPrint(message);
|
||||
}
|
||||
|
||||
void HostInterface::ReportMessage(const char* message)
|
||||
{
|
||||
Log_InfoPrintf(message);
|
||||
}
|
||||
|
||||
std::optional<std::vector<u8>> HostInterface::GetBIOSImage(ConsoleRegion region)
|
||||
{
|
||||
// Try the other default filenames in the directory of the configured BIOS.
|
||||
#define TRY_FILENAME(filename) \
|
||||
do \
|
||||
{ \
|
||||
std::string try_filename = filename; \
|
||||
std::optional<BIOS::Image> found_image = BIOS::LoadImageFromFile(try_filename); \
|
||||
BIOS::Hash found_hash = BIOS::GetHash(*found_image); \
|
||||
Log_DevPrintf("Hash for BIOS '%s': %s", try_filename.c_str(), found_hash.ToString().c_str()); \
|
||||
if (BIOS::IsValidHashForRegion(region, found_hash)) \
|
||||
{ \
|
||||
Log_InfoPrintf("Using BIOS from '%s'", try_filename.c_str()); \
|
||||
return found_image; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define RELATIVE_PATH(filename) std::filesystem::path(m_settings.bios_path).replace_filename(filename).string()
|
||||
|
||||
// Try the configured image.
|
||||
TRY_FILENAME(m_settings.bios_path);
|
||||
|
||||
// Try searching in the same folder for other region's images.
|
||||
switch (region)
|
||||
{
|
||||
case ConsoleRegion::NTSC_J:
|
||||
TRY_FILENAME(RELATIVE_PATH("scph1000.bin"));
|
||||
TRY_FILENAME(RELATIVE_PATH("scph5500.bin"));
|
||||
break;
|
||||
|
||||
case ConsoleRegion::NTSC_U:
|
||||
TRY_FILENAME(RELATIVE_PATH("scph1001.bin"));
|
||||
TRY_FILENAME(RELATIVE_PATH("scph5501.bin"));
|
||||
break;
|
||||
|
||||
case ConsoleRegion::PAL:
|
||||
TRY_FILENAME(RELATIVE_PATH("scph1002.bin"));
|
||||
TRY_FILENAME(RELATIVE_PATH("scph5502.bin"));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#undef RELATIVE_PATH
|
||||
#undef TRY_FILENAME
|
||||
|
||||
// Fall back to the default image.
|
||||
Log_WarningPrintf("No suitable BIOS image for region %s could be located, using configured image '%s'. This may "
|
||||
"result in instability.",
|
||||
Settings::GetConsoleRegionName(region), m_settings.bios_path.c_str());
|
||||
return BIOS::LoadImageFromFile(m_settings.bios_path);
|
||||
}
|
||||
|
||||
void HostInterface::ConnectControllers() {}
|
||||
|
||||
bool HostInterface::LoadState(const char* filename)
|
||||
{
|
||||
ByteStream* stream;
|
||||
if (!ByteStream_OpenFileStream(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED, &stream))
|
||||
return false;
|
||||
|
||||
ReportMessage(SmallString::FromFormat("Loading state from %s...", filename));
|
||||
AddOSDMessage(SmallString::FromFormat("Loading state from %s...", filename));
|
||||
|
||||
const bool result = m_system->LoadState(stream);
|
||||
if (!result)
|
||||
{
|
||||
ReportMessage(SmallString::FromFormat("Loading state from %s failed. Resetting.", filename));
|
||||
ReportError(SmallString::FromFormat("Loading state from %s failed. Resetting.", filename));
|
||||
m_system->Reset();
|
||||
}
|
||||
|
||||
|
@ -95,12 +143,12 @@ bool HostInterface::SaveState(const char* filename)
|
|||
const bool result = m_system->SaveState(stream);
|
||||
if (!result)
|
||||
{
|
||||
ReportMessage(SmallString::FromFormat("Saving state to %s failed.", filename));
|
||||
ReportError(SmallString::FromFormat("Saving state to %s failed.", filename));
|
||||
stream->Discard();
|
||||
}
|
||||
else
|
||||
{
|
||||
ReportMessage(SmallString::FromFormat("State saved to %s.", filename));
|
||||
AddOSDMessage(SmallString::FromFormat("State saved to %s.", filename));
|
||||
stream->Commit();
|
||||
}
|
||||
|
||||
|
@ -112,7 +160,7 @@ void HostInterface::UpdateAudioVisualSync()
|
|||
{
|
||||
const bool speed_limiter_enabled = m_settings.speed_limiter_enabled && !m_speed_limiter_temp_disabled;
|
||||
const bool audio_sync_enabled = speed_limiter_enabled;
|
||||
const bool vsync_enabled = !m_system || (speed_limiter_enabled && m_settings.gpu_vsync);
|
||||
const bool vsync_enabled = !m_system || m_paused || (speed_limiter_enabled && m_settings.gpu_vsync);
|
||||
Log_InfoPrintf("Syncing to %s%s", audio_sync_enabled ? "audio" : "",
|
||||
(speed_limiter_enabled && vsync_enabled) ? " and video" : "");
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#include "types.h"
|
||||
#include "settings.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
class AudioStream;
|
||||
class HostDisplay;
|
||||
|
@ -23,20 +25,26 @@ public:
|
|||
/// Returns a settings object which can be modified.
|
||||
Settings& GetSettings() { return m_settings; }
|
||||
|
||||
bool CreateSystem();
|
||||
bool BootSystem(const char* filename, const char* state_filename);
|
||||
void DestroySystem();
|
||||
|
||||
bool InitializeSystem(const char* filename, const char* exp1_filename);
|
||||
void ShutdownSystem();
|
||||
|
||||
|
||||
virtual void ReportMessage(const char* message) = 0;
|
||||
virtual void ReportError(const char* message);
|
||||
virtual void ReportMessage(const char* message);
|
||||
|
||||
// Adds OSD messages, duration is in seconds.
|
||||
virtual void AddOSDMessage(const char* message, float duration = 2.0f) = 0;
|
||||
|
||||
/// Loads the BIOS image for the specified region.
|
||||
virtual std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
|
||||
|
||||
bool LoadState(const char* filename);
|
||||
bool SaveState(const char* filename);
|
||||
|
||||
protected:
|
||||
/// Connects controllers. TODO: Clean this up later...
|
||||
virtual void ConnectControllers();
|
||||
|
||||
void UpdateAudioVisualSync();
|
||||
|
||||
std::unique_ptr<HostDisplay> m_display;
|
||||
|
|
|
@ -91,11 +91,39 @@ bool Settings::Save(const char* filename) const
|
|||
return true;
|
||||
}
|
||||
|
||||
static std::array<const char*, 4> s_console_region_names = {{"Auto", "NTSC-J", "NTSC-U", "PAL"}};
|
||||
static std::array<const char*, 4> s_console_region_display_names = {
|
||||
{"Auto-Detect", "NTSC-J (Japan)", "NTSC-U (US)", "PAL (Europe, Australia)"}};
|
||||
|
||||
std::optional<ConsoleRegion> Settings::ParseConsoleRegionName(const char* str)
|
||||
{
|
||||
int index = 0;
|
||||
for (const char* name : s_console_region_names)
|
||||
{
|
||||
if (strcasecmp(name, str) == 0)
|
||||
return static_cast<ConsoleRegion>(index);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const char* Settings::GetConsoleRegionName(ConsoleRegion region)
|
||||
{
|
||||
return s_console_region_names[static_cast<int>(region)];
|
||||
}
|
||||
|
||||
const char* Settings::GetConsoleRegionDisplayName(ConsoleRegion region)
|
||||
{
|
||||
return s_console_region_display_names[static_cast<int>(region)];
|
||||
}
|
||||
|
||||
static std::array<const char*, 3> s_gpu_renderer_names = {{"D3D11", "OpenGL", "Software"}};
|
||||
static std::array<const char*, 3> s_gpu_renderer_display_names = {
|
||||
{"Hardware (D3D11)", "Hardware (OpenGL)", "Software"}};
|
||||
|
||||
std::optional<Settings::GPURenderer> Settings::ParseRendererName(const char* str)
|
||||
std::optional<GPURenderer> Settings::ParseRendererName(const char* str)
|
||||
{
|
||||
int index = 0;
|
||||
for (const char* name : s_gpu_renderer_names)
|
||||
|
|
|
@ -4,16 +4,10 @@
|
|||
|
||||
struct Settings
|
||||
{
|
||||
enum class GPURenderer
|
||||
{
|
||||
HardwareD3D11,
|
||||
HardwareOpenGL,
|
||||
Software,
|
||||
Count
|
||||
};
|
||||
|
||||
Settings();
|
||||
|
||||
ConsoleRegion region = ConsoleRegion::NTSC_U;
|
||||
|
||||
bool start_paused = false;
|
||||
bool speed_limiter_enabled = true;
|
||||
|
||||
|
@ -52,6 +46,10 @@ struct Settings
|
|||
void Load(const char* filename);
|
||||
bool Save(const char* filename) const;
|
||||
|
||||
static std::optional<ConsoleRegion> ParseConsoleRegionName(const char* str);
|
||||
static const char* GetConsoleRegionName(ConsoleRegion region);
|
||||
static const char* GetConsoleRegionDisplayName(ConsoleRegion region);
|
||||
|
||||
static std::optional<GPURenderer> ParseRendererName(const char* str);
|
||||
static const char* GetRendererName(GPURenderer renderer);
|
||||
static const char* GetRendererDisplayName(GPURenderer renderer);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "system.h"
|
||||
#include "YBaseLib/AutoReleasePtr.h"
|
||||
#include "YBaseLib/Log.h"
|
||||
#include "bios.h"
|
||||
#include "bus.h"
|
||||
#include "cdrom.h"
|
||||
#include "common/state_wrapper.h"
|
||||
|
@ -19,15 +20,6 @@
|
|||
#include <imgui.h>
|
||||
Log_SetChannel(System);
|
||||
|
||||
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)
|
||||
{
|
||||
m_cpu = std::make_unique<CPU::Core>();
|
||||
|
@ -39,10 +31,32 @@ System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
|||
m_timers = std::make_unique<Timers>();
|
||||
m_spu = std::make_unique<SPU>();
|
||||
m_mdec = std::make_unique<MDEC>();
|
||||
m_region = host_interface->GetSettings().region;
|
||||
}
|
||||
|
||||
System::~System() = default;
|
||||
|
||||
std::optional<ConsoleRegion> System::GetRegionForCDImage(const CDImage* image)
|
||||
{
|
||||
// TODO: Implement me.
|
||||
return ConsoleRegion::NTSC_U;
|
||||
}
|
||||
|
||||
bool System::IsPSExe(const char* filename)
|
||||
{
|
||||
const StaticString filename_str(filename);
|
||||
return filename_str.EndsWith(".psexe", false) || filename_str.EndsWith(".exe", false);
|
||||
}
|
||||
|
||||
std::unique_ptr<System> System::Create(HostInterface* host_interface)
|
||||
{
|
||||
std::unique_ptr<System> system(new System(host_interface));
|
||||
if (!system->CreateGPU())
|
||||
return {};
|
||||
|
||||
return system;
|
||||
}
|
||||
|
||||
bool System::RecreateGPU()
|
||||
{
|
||||
// save current state
|
||||
|
@ -70,7 +84,83 @@ bool System::RecreateGPU()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool System::Initialize()
|
||||
bool System::Boot(const char* filename)
|
||||
{
|
||||
// Load CD image up and detect region.
|
||||
std::unique_ptr<CDImage> media;
|
||||
bool exe_boot = false;
|
||||
if (filename)
|
||||
{
|
||||
exe_boot = IsPSExe(filename);
|
||||
if (exe_boot)
|
||||
{
|
||||
if (m_region == ConsoleRegion::Auto)
|
||||
{
|
||||
Log_InfoPrintf("Defaulting to NTSC-U region for executable.");
|
||||
m_region = ConsoleRegion::NTSC_U;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_InfoPrintf("Loading CD image '%s'...", filename);
|
||||
media = CDImage::Open(filename);
|
||||
if (!media)
|
||||
{
|
||||
m_host_interface->ReportError(SmallString::FromFormat("Failed to load CD image '%s'", filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_region == ConsoleRegion::Auto)
|
||||
{
|
||||
std::optional<ConsoleRegion> detected_region = GetRegionForCDImage(media.get());
|
||||
m_region = detected_region.value_or(ConsoleRegion::NTSC_U);
|
||||
if (detected_region)
|
||||
Log_InfoPrintf("Auto-detected %s region for '%s'", Settings::GetConsoleRegionName(m_region));
|
||||
else
|
||||
Log_WarningPrintf("Could not determine region for CD. Defaulting to NTSC-U.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load BIOS image.
|
||||
std::optional<BIOS::Image> bios_image = m_host_interface->GetBIOSImage(m_region);
|
||||
if (!bios_image)
|
||||
{
|
||||
m_host_interface->ReportError(
|
||||
TinyString::FromFormat("Failed to load %s BIOS", Settings::GetConsoleRegionName(m_region)));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Component setup.
|
||||
InitializeComponents();
|
||||
UpdateMemoryCards();
|
||||
|
||||
// Enable tty by patching bios.
|
||||
const BIOS::Hash bios_hash = BIOS::GetHash(*bios_image);
|
||||
if (GetSettings().bios_patch_tty_enable)
|
||||
BIOS::PatchBIOSEnableTTY(*bios_image, bios_hash);
|
||||
|
||||
// Load EXE late after BIOS.
|
||||
if (exe_boot && !LoadEXE(filename, *bios_image))
|
||||
{
|
||||
m_host_interface->ReportError(SmallString::FromFormat("Failed to load EXE file '%s'", filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Insert CD, and apply fastboot patch if enabled.
|
||||
m_cdrom->InsertMedia(std::move(media));
|
||||
if (m_cdrom->HasMedia() && GetSettings().bios_patch_fast_boot)
|
||||
BIOS::PatchBIOSFastBoot(*bios_image, bios_hash);
|
||||
|
||||
// Load the patched BIOS up.
|
||||
m_bus->SetBIOS(*bios_image);
|
||||
|
||||
// Good to go.
|
||||
Reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
void System::InitializeComponents()
|
||||
{
|
||||
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(),
|
||||
|
@ -86,32 +176,23 @@ bool System::Initialize()
|
|||
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());
|
||||
|
||||
if (!CreateGPU())
|
||||
return false;
|
||||
|
||||
if (!LoadBIOS())
|
||||
return false;
|
||||
|
||||
UpdateMemoryCards();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool System::CreateGPU()
|
||||
{
|
||||
switch (m_host_interface->GetSettings().gpu_renderer)
|
||||
{
|
||||
case Settings::GPURenderer::HardwareOpenGL:
|
||||
case GPURenderer::HardwareOpenGL:
|
||||
m_gpu = GPU::CreateHardwareOpenGLRenderer();
|
||||
break;
|
||||
|
||||
#ifdef WIN32
|
||||
case Settings::GPURenderer::HardwareD3D11:
|
||||
case GPURenderer::HardwareD3D11:
|
||||
m_gpu = GPU::CreateHardwareD3D11Renderer();
|
||||
break;
|
||||
#endif
|
||||
|
||||
case Settings::GPURenderer::Software:
|
||||
case GPURenderer::Software:
|
||||
default:
|
||||
m_gpu = GPU::CreateSoftwareRenderer();
|
||||
break;
|
||||
|
@ -122,7 +203,7 @@ bool System::CreateGPU()
|
|||
{
|
||||
Log_ErrorPrintf("Failed to initialize GPU, falling back to software");
|
||||
m_gpu.reset();
|
||||
m_host_interface->GetSettings().gpu_renderer = Settings::GPURenderer::Software;
|
||||
m_host_interface->GetSettings().gpu_renderer = GPURenderer::Software;
|
||||
m_gpu = GPU::CreateSoftwareRenderer();
|
||||
if (!m_gpu->Initialize(m_host_interface->GetDisplay(), this, m_dma.get(), m_interrupt_controller.get(),
|
||||
m_timers.get()))
|
||||
|
@ -136,49 +217,6 @@ bool System::CreateGPU()
|
|||
return true;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (GetSettings().bios_patch_tty_enable)
|
||||
{
|
||||
Log_InfoPrintf("Patching BIOS to enable TTY/printf");
|
||||
m_bus->PatchBIOS(0x1FC06F0C, 0x24010001);
|
||||
m_bus->PatchBIOS(0x1FC06F14, 0xAF81A9C0);
|
||||
}
|
||||
|
||||
if (GetSettings().bios_patch_fast_boot)
|
||||
{
|
||||
Log_InfoPrintf("Patching BIOS for fast boot");
|
||||
|
||||
// Replace the shell entry point with a return back to the bootstrap.
|
||||
m_bus->PatchBIOS(0x1FC18000, 0x03E00008);
|
||||
m_bus->PatchBIOS(0x1FC18004, 0x00000000);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_WarningPrintf("Unknown BIOS version, not applying patches");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool System::DoState(StateWrapper& sw)
|
||||
{
|
||||
if (!sw.DoMarker("System"))
|
||||
|
@ -260,7 +298,7 @@ void System::RunFrame()
|
|||
}
|
||||
}
|
||||
|
||||
bool System::LoadEXE(const char* filename)
|
||||
bool System::LoadEXE(const char* filename, std::vector<u8>& bios_image)
|
||||
{
|
||||
#pragma pack(push, 1)
|
||||
struct EXEHeader
|
||||
|
@ -326,25 +364,11 @@ bool System::LoadEXE(const char* filename)
|
|||
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;
|
||||
return BIOS::PatchBIOSForEXE(bios_image, r_pc, r_gp, r_sp, r_fp);
|
||||
}
|
||||
|
||||
bool System::SetExpansionROM(const char* filename)
|
||||
|
@ -438,7 +462,12 @@ bool System::HasMedia() const
|
|||
|
||||
bool System::InsertMedia(const char* path)
|
||||
{
|
||||
return m_cdrom->InsertMedia(path);
|
||||
std::unique_ptr<CDImage> image = CDImage::Open(path);
|
||||
if (!image)
|
||||
return false;
|
||||
|
||||
m_cdrom->InsertMedia(std::move(image));
|
||||
return true;
|
||||
}
|
||||
|
||||
void System::RemoveMedia()
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
#include "types.h"
|
||||
#include "host_interface.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
class ByteStream;
|
||||
class CDImage;
|
||||
class StateWrapper;
|
||||
|
||||
namespace CPU {
|
||||
|
@ -24,9 +26,17 @@ class MDEC;
|
|||
class System
|
||||
{
|
||||
public:
|
||||
System(HostInterface* host_interface);
|
||||
~System();
|
||||
|
||||
/// Detects region for the specified image file.
|
||||
static std::optional<ConsoleRegion> GetRegionForCDImage(const CDImage* image);
|
||||
|
||||
/// Returns true if the filename is a PlayStation executable we can inject.
|
||||
static bool IsPSExe(const char* filename);
|
||||
|
||||
/// Creates a new System.
|
||||
static std::unique_ptr<System> Create(HostInterface* host_interface);
|
||||
|
||||
// Accessing components.
|
||||
HostInterface* GetHostInterface() const { return m_host_interface; }
|
||||
CPU::Core* GetCPU() const { return m_cpu.get(); }
|
||||
|
@ -48,7 +58,7 @@ public:
|
|||
|
||||
const Settings& GetSettings() { return m_host_interface->GetSettings(); }
|
||||
|
||||
bool Initialize();
|
||||
bool Boot(const char* filename);
|
||||
void Reset();
|
||||
|
||||
bool LoadState(ByteStream* state);
|
||||
|
@ -59,7 +69,7 @@ public:
|
|||
|
||||
void RunFrame();
|
||||
|
||||
bool LoadEXE(const char* filename);
|
||||
bool LoadEXE(const char* filename, std::vector<u8>& bios_image);
|
||||
bool SetExpansionROM(const char* filename);
|
||||
|
||||
void SetDowncount(TickCount downcount);
|
||||
|
@ -76,9 +86,12 @@ public:
|
|||
void RemoveMedia();
|
||||
|
||||
private:
|
||||
System(HostInterface* host_interface);
|
||||
|
||||
bool DoState(StateWrapper& sw);
|
||||
bool CreateGPU();
|
||||
bool LoadBIOS();
|
||||
|
||||
void InitializeComponents();
|
||||
|
||||
HostInterface* m_host_interface;
|
||||
std::unique_ptr<CPU::Core> m_cpu;
|
||||
|
@ -91,6 +104,7 @@ private:
|
|||
std::unique_ptr<Timers> m_timers;
|
||||
std::unique_ptr<SPU> m_spu;
|
||||
std::unique_ptr<MDEC> m_mdec;
|
||||
ConsoleRegion m_region = ConsoleRegion::NTSC_U;
|
||||
u32 m_frame_number = 1;
|
||||
u32 m_internal_frame_number = 1;
|
||||
u32 m_global_tick_counter = 0;
|
||||
|
|
|
@ -22,3 +22,19 @@ using TickCount = s32;
|
|||
static constexpr TickCount MASTER_CLOCK = 44100 * 0x300; // 33868800Hz or 33.8688MHz, also used as CPU clock
|
||||
static constexpr TickCount MAX_SLICE_SIZE = MASTER_CLOCK / 10;
|
||||
|
||||
enum class ConsoleRegion
|
||||
{
|
||||
Auto,
|
||||
NTSC_J,
|
||||
NTSC_U,
|
||||
PAL,
|
||||
Count
|
||||
};
|
||||
|
||||
enum class GPURenderer : u8
|
||||
{
|
||||
HardwareD3D11,
|
||||
HardwareOpenGL,
|
||||
Software,
|
||||
Count
|
||||
};
|
||||
|
|
|
@ -133,21 +133,7 @@ void SDLHostInterface::SaveSettings()
|
|||
m_settings.Save(m_settings_filename.c_str());
|
||||
}
|
||||
|
||||
bool SDLHostInterface::InitializeSystem(const char* filename, const char* exp1_filename)
|
||||
{
|
||||
if (!HostInterface::InitializeSystem(filename, exp1_filename))
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "System initialization failed.", m_window);
|
||||
return false;
|
||||
}
|
||||
|
||||
ConnectDevices();
|
||||
UpdateAudioVisualSync();
|
||||
m_paused = m_system->GetSettings().start_paused;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLHostInterface::ConnectDevices()
|
||||
void SDLHostInterface::ConnectControllers()
|
||||
{
|
||||
m_controller = DigitalController::Create();
|
||||
m_system->SetController(0, m_controller);
|
||||
|
@ -216,7 +202,7 @@ std::unique_ptr<SDLHostInterface> SDLHostInterface::Create(const char* filename
|
|||
const bool boot = (filename != nullptr || exp1_filename != nullptr || save_state_filename != nullptr);
|
||||
if (boot)
|
||||
{
|
||||
if (!intf->InitializeSystem(filename, exp1_filename))
|
||||
if (!intf->CreateSystem() || !intf->BootSystem(filename, exp1_filename))
|
||||
return nullptr;
|
||||
|
||||
if (save_state_filename)
|
||||
|
@ -235,9 +221,14 @@ TinyString SDLHostInterface::GetSaveStateFilename(u32 index)
|
|||
return TinyString::FromFormat("savestate_%u.bin", index);
|
||||
}
|
||||
|
||||
void SDLHostInterface::ReportError(const char* message)
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "DuckStation Error", message, m_window);
|
||||
}
|
||||
|
||||
void SDLHostInterface::ReportMessage(const char* message)
|
||||
{
|
||||
AddOSDMessage(message, 3.0f);
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "DuckStation Information", message, m_window);
|
||||
}
|
||||
|
||||
static inline u32 SDLButtonToHostButton(u32 button)
|
||||
|
@ -736,13 +727,13 @@ void SDLHostInterface::DrawQuickSettingsMenu()
|
|||
|
||||
if (ImGui::BeginMenu("Renderer"))
|
||||
{
|
||||
const Settings::GPURenderer current = m_settings.gpu_renderer;
|
||||
for (u32 i = 0; i < static_cast<u32>(Settings::GPURenderer::Count); i++)
|
||||
const GPURenderer current = m_settings.gpu_renderer;
|
||||
for (u32 i = 0; i < static_cast<u32>(GPURenderer::Count); i++)
|
||||
{
|
||||
if (ImGui::MenuItem(Settings::GetRendererDisplayName(static_cast<Settings::GPURenderer>(i)), nullptr,
|
||||
if (ImGui::MenuItem(Settings::GetRendererDisplayName(static_cast<GPURenderer>(i)), nullptr,
|
||||
i == static_cast<u32>(current)))
|
||||
{
|
||||
m_settings.gpu_renderer = static_cast<Settings::GPURenderer>(i);
|
||||
m_settings.gpu_renderer = static_cast<GPURenderer>(i);
|
||||
settings_changed = true;
|
||||
if (m_system)
|
||||
SwitchGPURenderer();
|
||||
|
@ -998,12 +989,12 @@ void SDLHostInterface::DrawSettingsWindow()
|
|||
if (ImGui::Combo(
|
||||
"##gpu_renderer", &gpu_renderer,
|
||||
[](void*, int index, const char** out_text) {
|
||||
*out_text = Settings::GetRendererDisplayName(static_cast<Settings::GPURenderer>(index));
|
||||
*out_text = Settings::GetRendererDisplayName(static_cast<GPURenderer>(index));
|
||||
return true;
|
||||
},
|
||||
nullptr, static_cast<int>(Settings::GPURenderer::Count)))
|
||||
nullptr, static_cast<int>(GPURenderer::Count)))
|
||||
{
|
||||
m_settings.gpu_renderer = static_cast<Settings::GPURenderer>(gpu_renderer);
|
||||
m_settings.gpu_renderer = static_cast<GPURenderer>(gpu_renderer);
|
||||
SwitchGPURenderer();
|
||||
}
|
||||
}
|
||||
|
@ -1213,21 +1204,16 @@ void SDLHostInterface::DoReset()
|
|||
void SDLHostInterface::DoPowerOff()
|
||||
{
|
||||
Assert(m_system);
|
||||
ShutdownSystem();
|
||||
DestroySystem();
|
||||
AddOSDMessage("System powered off.");
|
||||
}
|
||||
|
||||
void SDLHostInterface::DoResume()
|
||||
{
|
||||
Assert(!m_system);
|
||||
if (!InitializeSystem())
|
||||
return;
|
||||
|
||||
if (!LoadState(RESUME_SAVESTATE_FILENAME))
|
||||
if (!CreateSystem() || !BootSystem(nullptr, RESUME_SAVESTATE_FILENAME))
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Load state failed",
|
||||
"Failed to load the resume save state. Stopping emulation.", m_window);
|
||||
ShutdownSystem();
|
||||
DestroySystem();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1244,8 +1230,11 @@ void SDLHostInterface::DoStartDisc()
|
|||
return;
|
||||
|
||||
AddOSDMessage(SmallString::FromFormat("Starting disc from '%s'...", path));
|
||||
if (!InitializeSystem(path, nullptr))
|
||||
if (!CreateSystem() || !BootSystem(path, nullptr))
|
||||
{
|
||||
DestroySystem();
|
||||
return;
|
||||
}
|
||||
|
||||
ResetPerformanceCounters();
|
||||
ClearImGuiFocus();
|
||||
|
@ -1256,8 +1245,11 @@ void SDLHostInterface::DoStartBIOS()
|
|||
Assert(!m_system);
|
||||
|
||||
AddOSDMessage("Starting BIOS...");
|
||||
if (!InitializeSystem(nullptr, nullptr))
|
||||
if (!CreateSystem() || !BootSystem(nullptr, nullptr))
|
||||
{
|
||||
DestroySystem();
|
||||
return;
|
||||
}
|
||||
|
||||
ResetPerformanceCounters();
|
||||
ClearImGuiFocus();
|
||||
|
@ -1282,10 +1274,19 @@ void SDLHostInterface::DoChangeDisc()
|
|||
|
||||
void SDLHostInterface::DoLoadState(u32 index)
|
||||
{
|
||||
if (!HasSystem() && !InitializeSystem(nullptr, nullptr))
|
||||
return;
|
||||
|
||||
if (HasSystem())
|
||||
{
|
||||
LoadState(GetSaveStateFilename(index));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CreateSystem() || !BootSystem(nullptr, GetSaveStateFilename(index)))
|
||||
{
|
||||
DestroySystem();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ResetPerformanceCounters();
|
||||
ClearImGuiFocus();
|
||||
}
|
||||
|
@ -1321,16 +1322,15 @@ void SDLHostInterface::DoToggleSoftwareRendering()
|
|||
if (!m_system)
|
||||
return;
|
||||
|
||||
if (m_settings.gpu_renderer != Settings::GPURenderer::Software)
|
||||
if (m_settings.gpu_renderer != GPURenderer::Software)
|
||||
{
|
||||
m_settings.gpu_renderer = Settings::GPURenderer::Software;
|
||||
m_settings.gpu_renderer = GPURenderer::Software;
|
||||
AddOSDMessage("Switched to software GPU renderer.");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settings.gpu_renderer = m_display->GetRenderAPI() == HostDisplay::RenderAPI::D3D11 ?
|
||||
Settings::GPURenderer::HardwareD3D11 :
|
||||
Settings::GPURenderer::HardwareOpenGL;
|
||||
m_settings.gpu_renderer = m_display->GetRenderAPI() == HostDisplay::RenderAPI::D3D11 ? GPURenderer::HardwareD3D11 :
|
||||
GPURenderer::HardwareOpenGL;
|
||||
AddOSDMessage("Switched to hardware GPU renderer.");
|
||||
}
|
||||
|
||||
|
@ -1431,6 +1431,6 @@ void SDLHostInterface::Run()
|
|||
"Saving state failed, you will not be able to resume this session.", m_window);
|
||||
}
|
||||
|
||||
ShutdownSystem();
|
||||
DestroySystem();
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ class DigitalController;
|
|||
class MemoryCard;
|
||||
class AudioStream;
|
||||
|
||||
class SDLHostInterface : public HostInterface
|
||||
class SDLHostInterface final : public HostInterface
|
||||
{
|
||||
public:
|
||||
SDLHostInterface();
|
||||
|
@ -28,6 +28,7 @@ public:
|
|||
|
||||
static TinyString GetSaveStateFilename(u32 index);
|
||||
|
||||
void ReportError(const char* message) override;
|
||||
void ReportMessage(const char* message) override;
|
||||
|
||||
// Adds OSD messages, duration is in seconds.
|
||||
|
@ -35,6 +36,9 @@ public:
|
|||
|
||||
void Run();
|
||||
|
||||
protected:
|
||||
void ConnectControllers() override;
|
||||
|
||||
private:
|
||||
static constexpr u32 NUM_QUICK_SAVE_STATES = 10;
|
||||
static constexpr char RESUME_SAVESTATE_FILENAME[] = "savestate_resume.bin";
|
||||
|
@ -49,7 +53,7 @@ private:
|
|||
bool HasSystem() const { return static_cast<bool>(m_system); }
|
||||
|
||||
#ifdef WIN32
|
||||
bool UseOpenGLRenderer() const { return m_settings.gpu_renderer == Settings::GPURenderer::HardwareOpenGL; }
|
||||
bool UseOpenGLRenderer() const { return m_settings.gpu_renderer == GPURenderer::HardwareOpenGL; }
|
||||
#else
|
||||
bool UseOpenGLRenderer() const { return true; }
|
||||
#endif
|
||||
|
@ -64,8 +68,6 @@ private:
|
|||
|
||||
void SaveSettings();
|
||||
|
||||
bool InitializeSystem(const char* filename = nullptr, const char* exp1_filename = nullptr);
|
||||
void ConnectDevices();
|
||||
void ResetPerformanceCounters();
|
||||
void SwitchGPURenderer();
|
||||
void UpdateFullscreen();
|
||||
|
|
Loading…
Reference in a new issue