From ad652c47ed40990613b44f95b0e2c12565e474f3 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Fri, 20 Sep 2019 20:14:00 +1000 Subject: [PATCH] Basic CD image loading --- src/common/cd_image.cpp | 119 ++++++++++++++++++++++++++++++ src/common/cd_image.h | 59 +++++++++++++++ src/common/common.vcxproj | 2 + src/common/common.vcxproj.filters | 2 + src/pse-sdl/sdl_interface.cpp | 2 +- src/pse/cdrom.cpp | 29 ++++++++ src/pse/cdrom.h | 6 ++ src/pse/cpu_core.cpp | 13 +++- src/pse/cpu_core.inl | 4 +- src/pse/host_interface.cpp | 13 +++- src/pse/system.cpp | 15 ++++ src/pse/system.h | 4 + 12 files changed, 260 insertions(+), 8 deletions(-) create mode 100644 src/common/cd_image.cpp create mode 100644 src/common/cd_image.h diff --git a/src/common/cd_image.cpp b/src/common/cd_image.cpp new file mode 100644 index 000000000..51765adbf --- /dev/null +++ b/src/common/cd_image.cpp @@ -0,0 +1,119 @@ +#include "cd_image.h" +#include "YBaseLib/ByteStream.h" +#include "YBaseLib/Log.h" +Log_SetChannel(CDImage); + +CDImage::CDImage() = default; + +CDImage::~CDImage() +{ + if (m_data_file) + m_data_file->Release(); +} + +constexpr u64 CDImage::MSFToLBA(u32 minute, u32 second, u32 frame) +{ + return ZeroExtend64(minute) * FRAMES_PER_MINUTE + ZeroExtend64(second) * FRAMES_PER_SECOND + ZeroExtend64(frame); +} + +constexpr void CDImage::LBAToMSF(u64 lba, u32* minute, u32* second, u32* frame) +{ + const u32 offset = lba % FRAMES_PER_MINUTE; + *minute = Truncate32(lba / FRAMES_PER_MINUTE); + *second = Truncate32(offset / FRAMES_PER_SECOND); + *frame = Truncate32(offset % FRAMES_PER_SECOND); +} + +bool CDImage::Open(const char* path) +{ + Assert(!m_data_file); + + if (!ByteStream_OpenFileStream(path, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_SEEKABLE, &m_data_file)) + { + Log_ErrorPrintf("Failed to open '%s'", path); + return false; + } + + m_lba_count = m_data_file->GetSize() / RAW_SECTOR_SIZE; + return true; +} + +bool CDImage::Seek(u64 lba) +{ + if (lba >= m_lba_count) + return false; + + if (!m_data_file->SeekAbsolute(lba * RAW_SECTOR_SIZE)) + return false; + + m_current_lba = lba; + return true; +} + +bool CDImage::Seek(u32 minute, u32 second, u32 frame) +{ + return Seek(MSFToLBA(minute, second, frame)); +} + +u32 CDImage::Read(ReadMode read_mode, u64 lba, u32 sector_count, void* buffer) +{ + if (!Seek(lba)) + return false; + + return Read(read_mode, sector_count, buffer); +} + +u32 CDImage::Read(ReadMode read_mode, u32 minute, u32 second, u32 frame, u32 sector_count, void* buffer) +{ + if (!Seek(minute, second, frame)) + return false; + + return Read(read_mode, sector_count, buffer); +} + +u32 CDImage::Read(ReadMode read_mode, u32 sector_count, void* buffer) +{ + char* buffer_ptr = static_cast(buffer); + u32 sectors_read = 0; + for (; sectors_read < sector_count; sectors_read++) + { + if (m_current_lba == m_lba_count) + break; + + // get raw sector + char raw_sector[RAW_SECTOR_SIZE]; + if (!m_data_file->Read2(raw_sector, RAW_SECTOR_SIZE)) + { + Log_ErrorPrintf("Read of LBA %llu failed", m_current_lba); + m_data_file->SeekAbsolute(m_current_lba * RAW_SECTOR_SIZE); + return false; + } + + switch (read_mode) + { + case ReadMode::DataOnly: + std::memcpy(buffer_ptr, raw_sector + SECTOR_SYNC_SIZE, DATA_SECTOR_SIZE); + buffer_ptr += DATA_SECTOR_SIZE; + break; + + case ReadMode::RawNoSync: + std::memcpy(buffer_ptr, raw_sector + SECTOR_SYNC_SIZE, RAW_SECTOR_SIZE - SECTOR_SYNC_SIZE); + buffer_ptr += RAW_SECTOR_SIZE - SECTOR_SYNC_SIZE; + break; + + case ReadMode::RawSector: + std::memcpy(buffer_ptr, raw_sector, RAW_SECTOR_SIZE); + buffer_ptr += RAW_SECTOR_SIZE; + break; + + default: + UnreachableCode(); + break; + } + + m_current_lba++; + sectors_read++; + } + + return sectors_read; +} diff --git a/src/common/cd_image.h b/src/common/cd_image.h new file mode 100644 index 000000000..a0ef098a1 --- /dev/null +++ b/src/common/cd_image.h @@ -0,0 +1,59 @@ +#pragma once +#include "types.h" + +class ByteStream; + +class CDImage +{ +public: + CDImage(); + ~CDImage(); + + enum class ReadMode : u32 + { + DataOnly, // 2048 bytes per sector. + RawSector, // 2352 bytes per sector. + RawNoSync, // 2340 bytes per sector. + }; + + // Conversion helpers. + static constexpr u64 MSFToLBA(u32 minute, u32 second, u32 frame); + static constexpr void LBAToMSF(u64 lba, u32* minute, u32* second, u32* frame); + + // Accessors. + u64 GetCurrentLBA() const { return m_current_lba; } + u64 GetLBACount() const { return m_lba_count; } + + bool Open(const char* path); + + // Seek to data LBA. + bool Seek(u64 lba); + + // Seek to audio timestamp (MSF). + bool Seek(u32 minute, u32 second, u32 frame); + + // Seek and read at the same time. + u32 Read(ReadMode read_mode, u64 lba, u32 sector_count, void* buffer); + u32 Read(ReadMode read_mode, u32 minute, u32 second, u32 frame, u32 sector_count, void* buffer); + + // Read from the current LBA. Returns the number of sectors read. + u32 Read(ReadMode read_mode, u32 sector_count, void* buffer); + +private: + enum : u32 + { + RAW_SECTOR_SIZE = 2352, + DATA_SECTOR_SIZE = 2048, + SECTOR_SYNC_SIZE = 12, + FRAMES_PER_SECOND = 75, // "sectors" + SECONDS_PER_MINUTE = 60, + FRAMES_PER_MINUTE = FRAMES_PER_SECOND * SECONDS_PER_MINUTE, + }; + + // TODO: Multiple data files from cue sheet + ByteStream* m_data_file = nullptr; + + // Current LBA/total LBAs. + u64 m_current_lba = 0; + u64 m_lba_count = 0; +}; diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index b01ef1987..7b61e3387 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -37,6 +37,7 @@ + @@ -57,6 +58,7 @@ + diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index d1e2b8784..92340bf5e 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters @@ -20,6 +20,7 @@ + @@ -36,6 +37,7 @@ + diff --git a/src/pse-sdl/sdl_interface.cpp b/src/pse-sdl/sdl_interface.cpp index 360d0762c..314542221 100644 --- a/src/pse-sdl/sdl_interface.cpp +++ b/src/pse-sdl/sdl_interface.cpp @@ -67,7 +67,7 @@ static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLen Log_InfoPrintf(message); break; case GL_DEBUG_SEVERITY_NOTIFICATION: - Log_DebugPrint(message); + // Log_DebugPrint(message); break; } } diff --git a/src/pse/cdrom.cpp b/src/pse/cdrom.cpp index d2948d882..f2a46d1aa 100644 --- a/src/pse/cdrom.cpp +++ b/src/pse/cdrom.cpp @@ -1,5 +1,6 @@ #include "cdrom.h" #include "YBaseLib/Log.h" +#include "common/cd_image.h" #include "common/state_wrapper.h" #include "interrupt_controller.h" Log_SetChannel(CDROM); @@ -43,6 +44,34 @@ bool CDROM::DoState(StateWrapper& sw) return !sw.HasError(); } +bool CDROM::InsertMedia(const char* filename) +{ + auto media = std::make_unique(); + if (!media->Open(filename)) + { + Log_ErrorPrintf("Failed to open media at '%s'", filename); + return false; + } + + if (HasMedia()) + RemoveMedia(); + + m_media = std::move(media); + m_secondary_status.shell_open = false; + return true; +} + +void CDROM::RemoveMedia() +{ + if (!m_media) + return; + + // TODO: Error while reading? + Log_InfoPrintf("Removing CD..."); + m_media.reset(); + m_secondary_status.shell_open = true; +} + u8 CDROM::ReadRegister(u32 offset) { switch (offset) diff --git a/src/pse/cdrom.h b/src/pse/cdrom.h index af72d2c62..20767940a 100644 --- a/src/pse/cdrom.h +++ b/src/pse/cdrom.h @@ -3,6 +3,7 @@ #include "common/bitfield.h" #include "common/fifo_queue.h" +class CDImage; class StateWrapper; class DMA; @@ -18,6 +19,10 @@ public: void Reset(); bool DoState(StateWrapper& sw); + bool HasMedia() const { return static_cast(m_media); } + bool InsertMedia(const char* filename); + void RemoveMedia(); + // I/O u8 ReadRegister(u32 offset); void WriteRegister(u32 offset, u8 value); @@ -84,6 +89,7 @@ private: DMA* m_dma; InterruptController* m_interrupt_controller; + std::unique_ptr m_media; enum class State : u32 { diff --git a/src/pse/cpu_core.cpp b/src/pse/cpu_core.cpp index 397f779e0..2373ca7cb 100644 --- a/src/pse/cpu_core.cpp +++ b/src/pse/cpu_core.cpp @@ -184,6 +184,9 @@ void Core::Branch(u32 target) u32 Core::GetExceptionVector(Exception excode) const { const u32 base = m_cop0_regs.sr.BEV ? UINT32_C(0xbfc00100) : UINT32_C(0x80000000); + +#if 0 + // apparently this isn't correct... switch (excode) { case Exception::BP: @@ -192,6 +195,9 @@ u32 Core::GetExceptionVector(Exception excode) const default: return base | UINT32_C(0x00000080); } +#else + return base | UINT32_C(0x00000080); +#endif } void Core::RaiseException(Exception excode, u8 coprocessor /* = 0 */) @@ -344,14 +350,15 @@ bool Core::FetchInstruction() { m_regs.pc = m_regs.npc; - if (!DoAlignmentCheck(static_cast(m_regs.npc))) + if (!DoAlignmentCheck( + static_cast(m_regs.npc)) || + !DoMemoryAccess( + static_cast(m_regs.npc), m_next_instruction.bits)) { // this will call FetchInstruction() again when the pipeline is flushed. return false; } - DoMemoryAccess( - static_cast(m_regs.npc), m_next_instruction.bits); m_regs.npc += sizeof(m_next_instruction.bits); return true; } diff --git a/src/pse/cpu_core.inl b/src/pse/cpu_core.inl index c3269e4ca..386b93f8a 100644 --- a/src/pse/cpu_core.inl +++ b/src/pse/cpu_core.inl @@ -39,7 +39,7 @@ bool Core::DoMemoryAccess(VirtualMemoryAddress address, u32& value) case 0x03: // KUSEG 1536M-2048M { // Above 512mb raises an exception. - Panic("Bad user access"); + RaiseException(is_instruction_fetch ? Exception::IBE : Exception::DBE); return false; } @@ -95,7 +95,7 @@ bool Core::DoMemoryAccess(VirtualMemoryAddress address, u32& value) } else { - Panic("KSEG2 access"); + RaiseException(is_instruction_fetch ? Exception::IBE : Exception::DBE); return false; } } diff --git a/src/pse/host_interface.cpp b/src/pse/host_interface.cpp index 8fd92257a..9791a8b31 100644 --- a/src/pse/host_interface.cpp +++ b/src/pse/host_interface.cpp @@ -24,10 +24,19 @@ bool HostInterface::InitializeSystem(const char* filename, const char* save_stat const StaticString filename_str(filename); if (filename_str.EndsWith(".psxexe", false) || filename_str.EndsWith(".exe", false)) { - Log_InfoPrintf("Sideloading EXE file %s", filename); + Log_InfoPrintf("Sideloading EXE file '%s'", filename); if (!m_system->LoadEXE(filename)) { - Log_ErrorPrintf("Failed to load EXE file %s", 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; } } diff --git a/src/pse/system.cpp b/src/pse/system.cpp index 9c3573db6..9df5a9125 100644 --- a/src/pse/system.cpp +++ b/src/pse/system.cpp @@ -213,3 +213,18 @@ void System::SetPadDevice(u32 slot, std::shared_ptr dev) { m_pad->SetDevice(slot, std::move(dev)); } + +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(); +} diff --git a/src/pse/system.h b/src/pse/system.h index 81884965a..f08ab02fe 100644 --- a/src/pse/system.h +++ b/src/pse/system.h @@ -45,6 +45,10 @@ public: void SetPadDevice(u32 slot, std::shared_ptr dev); + bool HasMedia() const; + bool InsertMedia(const char* path); + void RemoveMedia(); + private: bool DoState(StateWrapper& sw);