diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 30fce0bc68..3e58c33db9 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include #include "Common/CommonPaths.h" #include "Common/CommonTypes.h" @@ -32,6 +33,15 @@ #include "DiscIO/NANDContentLoader.h" #include "DiscIO/VolumeCreator.h" +bool CBoot::DVDRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt) +{ + std::vector buffer(length); + if (!DVDInterface::GetVolume().Read(dvd_offset, length, buffer.data(), decrypt)) + return false; + Memory::CopyToEmu(output_address, buffer.data(), length); + return true; +} + void CBoot::Load_FST(bool _bIsWii) { if (!DVDInterface::VolumeIsValid()) @@ -40,7 +50,7 @@ void CBoot::Load_FST(bool _bIsWii) const DiscIO::IVolume& volume = DVDInterface::GetVolume(); // copy first 20 bytes of disc to start of Mem 1 - DVDInterface::DVDRead(/*offset*/0, /*address*/0, /*length*/0x20, false); + DVDRead(/*offset*/0, /*address*/0, /*length*/0x20, false); // copy of game id Memory::Write_U32(Memory::Read_U32(0x0000), 0x3180); @@ -57,7 +67,7 @@ void CBoot::Load_FST(bool _bIsWii) Memory::Write_U32(arenaHigh, 0x00000034); // load FST - DVDInterface::DVDRead(fstOffset, arenaHigh, fstSize, _bIsWii); + DVDRead(fstOffset, arenaHigh, fstSize, _bIsWii); Memory::Write_U32(arenaHigh, 0x00000038); Memory::Write_U32(maxFstSize, 0x0000003c); } diff --git a/Source/Core/Core/Boot/Boot.h b/Source/Core/Core/Boot/Boot.h index fbf221b7a6..41815c00f4 100644 --- a/Source/Core/Core/Boot/Boot.h +++ b/Source/Core/Core/Boot/Boot.h @@ -41,6 +41,7 @@ public: std::string* title_id = nullptr); private: + static bool DVDRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt); static void RunFunction(u32 _iAddr); static void UpdateDebugger_MapLoaded(); diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index 6ee859c73a..c8f6f0d270 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -59,7 +59,7 @@ bool CBoot::EmulatedBS2_GC(bool skipAppLoader) // It's possible to boot DOL and ELF files without a disc inserted if (DVDInterface::VolumeIsValid()) - DVDInterface::DVDRead(/*offset*/0x00000000, /*address*/0x00000000, 0x20, false); // write disc info + DVDRead(/*offset*/0x00000000, /*address*/0x00000000, 0x20, false); // write disc info PowerPC::HostWrite_U32(0x0D15EA5E, 0x80000020); // Booted from bootrom. 0xE5207C22 = booted from jtag PowerPC::HostWrite_U32(Memory::REALRAM_SIZE, 0x80000028); // Physical Memory Size (24MB on retail) @@ -98,7 +98,7 @@ bool CBoot::EmulatedBS2_GC(bool skipAppLoader) INFO_LOG(BOOT, "GC BS2: Not running apploader!"); return false; } - DVDInterface::DVDRead(iAppLoaderOffset + 0x20, 0x01200000, iAppLoaderSize, false); + DVDRead(iAppLoaderOffset + 0x20, 0x01200000, iAppLoaderSize, false); // Setup pointers like real BS2 does if (SConfig::GetInstance().bNTSC) @@ -149,7 +149,7 @@ bool CBoot::EmulatedBS2_GC(bool skipAppLoader) u32 iDVDOffset = PowerPC::Read_U32(0x8130000c); INFO_LOG(MASTER_LOG, "DVDRead: offset: %08x memOffset: %08x length: %i", iDVDOffset, iRamAddress, iLength); - DVDInterface::DVDRead(iDVDOffset, iRamAddress, iLength, false); + DVDRead(iDVDOffset, iRamAddress, iLength, false); } while (PowerPC::ppcState.gpr[3] != 0x00); @@ -256,7 +256,7 @@ bool CBoot::SetupWiiMemory(DiscIO::IVolume::ECountry country) // When booting a WAD or the system menu, there will probably not be a disc inserted if (DVDInterface::VolumeIsValid()) - DVDInterface::DVDRead(0x00000000, 0x00000000, 0x20, false); // Game Code + DVDRead(0x00000000, 0x00000000, 0x20, false); // Game Code Memory::Write_U32(0x0D15EA5E, 0x00000020); // Another magic word Memory::Write_U32(0x00000001, 0x00000024); // Unknown @@ -333,10 +333,10 @@ bool CBoot::EmulatedBS2_Wii() if (DVDInterface::VolumeIsValid() && DVDInterface::GetVolume().GetVolumeType() == DiscIO::IVolume::WII_DISC) { // This is some kind of consistency check that is compared to the 0x00 - // values as the game boots. This location keep the 4 byte ID for as long + // values as the game boots. This location keeps the 4 byte ID for as long // as the game is running. The 6 byte ID at 0x00 is overwritten sometime // after this check during booting. - DVDInterface::DVDRead(0, 0x3180, 4, true); + DVDRead(0, 0x3180, 4, true); // Set up MSR and the BAT SPR registers. UReg_MSR& m_MSR = ((UReg_MSR&)PowerPC::ppcState.msr); @@ -376,7 +376,7 @@ bool CBoot::EmulatedBS2_Wii() ERROR_LOG(BOOT, "Invalid apploader. Probably your image is corrupted."); return false; } - DVDInterface::DVDRead(iAppLoaderOffset + 0x20, 0x01200000, iAppLoaderSize, true); + DVDRead(iAppLoaderOffset + 0x20, 0x01200000, iAppLoaderSize, true); //call iAppLoaderEntry DEBUG_LOG(BOOT, "Call iAppLoaderEntry"); @@ -414,7 +414,7 @@ bool CBoot::EmulatedBS2_Wii() u32 iDVDOffset = PowerPC::Read_U32(0x8130000c) << 2; INFO_LOG(BOOT, "DVDRead: offset: %08x memOffset: %08x length: %i", iDVDOffset, iRamAddress, iLength); - DVDInterface::DVDRead(iDVDOffset, iRamAddress, iLength, true); + DVDRead(iDVDOffset, iRamAddress, iLength, true); } while (PowerPC::ppcState.gpr[3] != 0x00); // iAppLoaderClose diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index bd69afeb80..99bc5ced18 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -83,6 +83,7 @@ set(SRCS ActionReplay.cpp HW/DSPLLE/DSPLLE.cpp HW/DSPLLE/DSPLLETools.cpp HW/DVDInterface.cpp + HW/DVDThread.cpp HW/EXI_Channel.cpp HW/EXI.cpp HW/EXI_Device.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 5e54a4fab7..05af583ed2 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -115,6 +115,7 @@ + @@ -325,6 +326,7 @@ + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 92d88bc479..a374defc07 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -315,6 +315,9 @@ HW %28Flipper/Hollywood%29\DI - Drive Interface + + HW %28Flipper/Hollywood%29\DI - Drive Interface + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes @@ -850,6 +853,9 @@ HW %28Flipper/Hollywood%29\DI - Drive Interface + + HW %28Flipper/Hollywood%29\DI - Drive Interface + HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index e0356ec12a..1dfa08124b 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -19,6 +19,7 @@ #include "Core/Movie.h" #include "Core/HW/AudioInterface.h" #include "Core/HW/DVDInterface.h" +#include "Core/HW/DVDThread.h" #include "Core/HW/Memmap.h" #include "Core/HW/MMIO.h" #include "Core/HW/ProcessorInterface.h" @@ -220,22 +221,6 @@ union UDICFG UDICFG(u32 _hex) {Hex = _hex;} }; -struct DVDReadCommand -{ - bool is_valid; - - u64 DVD_offset; - u32 output_address; - u32 length; - bool decrypt; - - DIInterruptType interrupt_type; - - // Used to notify emulated software after executing command. - // Pointers don't work with savestates, so CoreTiming events are used instead - int callback_event_type; -}; - static std::unique_ptr s_inserted_volume; // STATE_TO_SAVE @@ -249,8 +234,6 @@ static UDICR m_DICR; static UDIIMMBUF m_DIIMMBUF; static UDICFG m_DICFG; -static DVDReadCommand current_read_command; - static u32 AudioPos; static u32 CurrentStart; static u32 CurrentLength; @@ -263,7 +246,6 @@ static bool g_bDiscInside = false; bool g_bStream = false; static bool g_bStopAtTrackEnd = false; static int finish_execute_command = 0; -static int finish_execute_read_command = 0; static int dtk = 0; static u64 g_last_read_offset; @@ -284,8 +266,8 @@ void UpdateInterrupts(); void GenerateDIInterrupt(DIInterruptType _DVDInterrupt); void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF); -DVDReadCommand ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, - bool decrypt, DIInterruptType* interrupt_type, u64* ticks_until_completion); +bool ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, bool decrypt, + int callback_event_type, DIInterruptType* interrupt_type, u64* ticks_until_completion); u64 SimulateDiscReadTime(u64 offset, u32 length); s64 CalculateRawDiscReadTime(u64 offset, s64 length); @@ -301,8 +283,6 @@ void DoState(PointerWrap &p) p.Do(m_DIIMMBUF); p.DoPOD(m_DICFG); - p.Do(current_read_command); - p.Do(NextStart); p.Do(AudioPos); p.Do(NextLength); @@ -318,6 +298,8 @@ void DoState(PointerWrap &p) p.Do(g_last_read_time); p.Do(g_bStopAtTrackEnd); + + DVDThread::DoState(p); } static void FinishExecuteCommand(u64 userdata, int cyclesLate) @@ -330,32 +312,11 @@ static void FinishExecuteCommand(u64 userdata, int cyclesLate) } } -static void FinishExecuteReadCommand(u64 userdata, int cyclesLate) -{ - if (!current_read_command.is_valid) - { - PanicAlert("DVDInterface: There is no command to execute!"); - } - else - { - // Here is the actual disc reading - if (!DVDRead(current_read_command.DVD_offset, current_read_command.output_address, - current_read_command.length, current_read_command.decrypt)) - { - PanicAlertT("Can't read from DVD_Plugin - DVD-Interface: Fatal Error"); - } - } - - // The command is marked as invalid because it shouldn't be used again - current_read_command.is_valid = false; - - // The final step is to notify the emulated software that the command has been executed - CoreTiming::ScheduleEvent_Immediate(current_read_command.callback_event_type, - current_read_command.interrupt_type); -} - static u32 ProcessDTKSamples(short *tempPCM, u32 num_samples) { + // TODO: Read audio data using the DVD thread instead of blocking on it? + DVDThread::WaitUntilIdle(); + u32 samples_processed = 0; do { @@ -417,6 +378,8 @@ static void DTKStreamingCallback(u64 userdata, int cyclesLate) void Init() { + DVDThread::Start(); + m_DISR.Hex = 0; m_DICVR.Hex = 1; // Disc Channel relies on cover being open when no disc is inserted m_DICMDBUF[0].Hex = 0; @@ -429,8 +392,6 @@ void Init() m_DICFG.Hex = 0; m_DICFG.CONFIG = 1; // Disable bootrom descrambler - current_read_command.is_valid = false; - AudioPos = 0; NextStart = 0; NextLength = 0; @@ -449,7 +410,6 @@ void Init() insertDisc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback); finish_execute_command = CoreTiming::RegisterEvent("FinishExecuteCommand", FinishExecuteCommand); - finish_execute_read_command = CoreTiming::RegisterEvent("FinishExecuteReadCommand", FinishExecuteReadCommand); dtk = CoreTiming::RegisterEvent("StreamingTimer", DTKStreamingCallback); CoreTiming::ScheduleEvent(0, dtk); @@ -457,6 +417,7 @@ void Init() void Shutdown() { + DVDThread::Stop(); s_inserted_volume.reset(); } @@ -467,12 +428,14 @@ const DiscIO::IVolume& GetVolume() bool SetVolumeName(const std::string& disc_path) { + DVDThread::WaitUntilIdle(); s_inserted_volume = std::unique_ptr(DiscIO::CreateVolumeFromFilename(disc_path)); return VolumeIsValid(); } bool SetVolumeDirectory(const std::string& full_path, bool is_wii, const std::string& apploader_path, const std::string& DOL_path) { + DVDThread::WaitUntilIdle(); s_inserted_volume = std::unique_ptr(DiscIO::CreateVolumeFromDirectory(full_path, is_wii, apploader_path, DOL_path)); return VolumeIsValid(); } @@ -501,9 +464,9 @@ bool IsDiscInside() // that the userdata string exists when called void EjectDiscCallback(u64 userdata, int cyclesLate) { - // Empty the drive - SetDiscInside(false); + DVDThread::WaitUntilIdle(); s_inserted_volume.reset(); + SetDiscInside(false); } void InsertDiscCallback(u64 userdata, int cyclesLate) @@ -551,13 +514,9 @@ void SetLidOpen(bool _bOpen) GenerateDIInterrupt(INT_CVRINT); } -bool DVDRead(u64 _iDVDOffset, u32 _iRamAddress, u32 _iLength, bool decrypt) -{ - return s_inserted_volume->Read(_iDVDOffset, _iLength, Memory::GetPointer(_iRamAddress), decrypt); -} - bool ChangePartition(u64 offset) { + DVDThread::WaitUntilIdle(); return s_inserted_volume->ChangePartition(offset); } @@ -687,19 +646,21 @@ void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF) Memory::Write_U32(value, output_address); } -// If the returned DVDReadCommand has is_valid set to true, -// FinishExecuteReadCommand must be used to finish executing it -DVDReadCommand ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, - bool decrypt, DIInterruptType* interrupt_type, u64* ticks_until_completion) +// Iff false is returned, ScheduleEvent must be used to finish executing the command +bool ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, bool decrypt, + int callback_event_type, DIInterruptType* interrupt_type, u64* ticks_until_completion) { - DVDReadCommand command; - if (!g_bDiscInside) { + // Disc read fails g_ErrorCode = ERROR_NO_DISK | ERROR_COVER_H; *interrupt_type = INT_DEINT; - command.is_valid = false; - return command; + return false; + } + else + { + // Disc read succeeds + *interrupt_type = INT_TCINT; } if (DVD_length > output_length) @@ -714,13 +675,9 @@ DVDReadCommand ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_le else *ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length); - *interrupt_type = INT_TCINT; - command.is_valid = true; - command.DVD_offset = DVD_offset; - command.output_address = output_address; - command.length = DVD_length; - command.decrypt = decrypt; - return command; + DVDThread::StartRead(DVD_offset, output_address, DVD_length, decrypt, + callback_event_type, (int)*ticks_until_completion); + return true; } // When the command has finished executing, callback_event_type @@ -731,8 +688,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr { DIInterruptType interrupt_type = INT_TCINT; u64 ticks_until_completion = SystemTimers::GetTicksPerSecond() / 15000; - DVDReadCommand read_command; - read_command.is_valid = false; + bool command_handled_by_thread = false; bool GCAM = (SConfig::GetInstance().m_SIDevice[0] == SIDEVICE_AM_BASEBOARD) && (SConfig::GetInstance().m_EXIDevice[2] == EXIDEVICE_AM_BASEBOARD); @@ -777,15 +733,15 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr // Only seems to be used from WII_IPC, not through direct access case DVDLowReadDiskID: INFO_LOG(DVDINTERFACE, "DVDLowReadDiskID"); - read_command = ExecuteReadCommand(0, output_address, 0x20, output_length, - false, &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand(0, output_address, 0x20, output_length, false, + callback_event_type, &interrupt_type, &ticks_until_completion); break; // Only used from WII_IPC. This is the only read command that decrypts data case DVDLowRead: INFO_LOG(DVDINTERFACE, "DVDLowRead: DVDAddr: 0x%09" PRIx64 ", Size: 0x%x", (u64)command_2 << 2, command_1); - read_command = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, - true, &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, true, + callback_event_type, &interrupt_type, &ticks_until_completion); break; // Probably only used by Wii @@ -864,8 +820,8 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr (command_2 > 0x7ed40000 && command_2 < 0x7ed40008) || (((command_2 + command_1) > 0x7ed40000) && (command_2 + command_1) < 0x7ed40008))) { - read_command = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, - false, &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, false, + callback_event_type, &interrupt_type, &ticks_until_completion); } else { @@ -961,15 +917,15 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr } } - read_command = ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, - false, &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, false, + callback_event_type, &interrupt_type, &ticks_until_completion); } break; case 0x40: // Read DiscID INFO_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(output_address)); - read_command = ExecuteReadCommand(0, output_address, 0x20, output_length, - false, &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand(0, output_address, 0x20, output_length, false, + callback_event_type, &interrupt_type, &ticks_until_completion); break; default: @@ -1304,23 +1260,10 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr break; } - // The command will finish executing after a delay, + // The command will finish executing after a delay // to simulate the speed of a real disc drive - if (read_command.is_valid) - { - // We schedule a FinishExecuteReadCommand (which will call the actual callback - // once it's done) so that the data transfer isn't completed too early. - // Most games don't care about it, but if it's done wrong, Resident Evil 3 - // plays some extra noise when playing the menu selection sound effect. - read_command.callback_event_type = callback_event_type; - read_command.interrupt_type = interrupt_type; - current_read_command = read_command; - CoreTiming::ScheduleEvent((int)ticks_until_completion, finish_execute_read_command); - } - else - { + if (!command_handled_by_thread) CoreTiming::ScheduleEvent((int)ticks_until_completion, callback_event_type, interrupt_type); - } } // Simulates the timing aspects of reading data from a disc. diff --git a/Source/Core/Core/HW/DVDInterface.h b/Source/Core/Core/HW/DVDInterface.h index 583f3074f5..c931e128ff 100644 --- a/Source/Core/Core/HW/DVDInterface.h +++ b/Source/Core/Core/HW/DVDInterface.h @@ -105,7 +105,6 @@ bool IsDiscInside(); void ChangeDisc(const std::string& fileName); // DVD Access Functions -bool DVDRead(u64 _iDVDOffset, u32 _iRamAddress, u32 _iLength, bool decrypt); extern bool g_bStream; bool ChangePartition(u64 offset); void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address, u32 output_length, diff --git a/Source/Core/Core/HW/DVDThread.cpp b/Source/Core/Core/HW/DVDThread.cpp new file mode 100644 index 0000000000..d1f68205c0 --- /dev/null +++ b/Source/Core/Core/HW/DVDThread.cpp @@ -0,0 +1,178 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include +#include + +#include "Common/ChunkFile.h" +#include "Common/CommonTypes.h" +#include "Common/Event.h" +#include "Common/Flag.h" +#include "Common/MsgHandler.h" +#include "Common/Thread.h" +#include "Common/Timer.h" +#include "Common/Logging/Log.h" + +#include "Core/Core.h" +#include "Core/CoreTiming.h" +#include "Core/HW/DVDInterface.h" +#include "Core/HW/DVDThread.h" +#include "Core/HW/Memmap.h" +#include "Core/HW/SystemTimers.h" + +#include "DiscIO/Volume.h" + +namespace DVDThread +{ + +static void DVDThread(); + +static void FinishRead(u64 userdata, int cyclesLate); +static int s_finish_read; + +static std::thread s_dvd_thread; +static Common::Event s_dvd_thread_start_working; +static Common::Event s_dvd_thread_done_working; +static Common::Flag s_dvd_thread_exiting(false); + +static std::vector s_dvd_buffer; +static u64 s_time_read_started; +static bool s_dvd_success; + +static u64 s_dvd_offset; +static u32 s_output_address; +static u32 s_length; +static bool s_decrypt; + +// Used to notify emulated software after executing command. +// Pointers don't work with savestates, so CoreTiming events are used instead +static int s_callback_event_type; + +// The following time variables are only used for logging +static u64 s_realtime_started_us; +static u64 s_realtime_done_us; + +void Start() +{ + s_finish_read = CoreTiming::RegisterEvent("FinishReadDVDThread", FinishRead); + _assert_(!s_dvd_thread.joinable()); + s_dvd_thread = std::thread(DVDThread); +} + +void Stop() +{ + _assert_(s_dvd_thread.joinable()); + + // The DVD thread will return if s_DVD_thread_exiting + // is set when it starts working + s_dvd_thread_exiting.Set(); + s_dvd_thread_start_working.Set(); + + s_dvd_thread.join(); + + s_dvd_thread_exiting.Clear(); +} + +void DoState(PointerWrap &p) +{ + WaitUntilIdle(); + + // TODO: Savestates can be smaller if s_DVD_buffer is not saved + p.Do(s_dvd_buffer); + p.Do(s_time_read_started); + p.Do(s_dvd_success); + + p.Do(s_dvd_offset); + p.Do(s_output_address); + p.Do(s_length); + p.Do(s_decrypt); + p.Do(s_callback_event_type); + + // s_realtime_started_us and s_realtime_done_us aren't savestated + // because they rely on the current system's time. + // This means that loading a savestate might cause + // incorrect times to be logged once. +} + +void WaitUntilIdle() +{ + _assert_(Core::IsCPUThread()); + + // Wait until DVD thread isn't working + s_dvd_thread_done_working.Wait(); + + // Set the event again so that we still know that the DVD thread isn't working + s_dvd_thread_done_working.Set(); +} + +void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt, + int callback_event_type, int ticks_until_completion) +{ + _assert_(Core::IsCPUThread()); + + s_dvd_thread_done_working.Wait(); + + s_dvd_offset = dvd_offset; + s_output_address = output_address; + s_length = length; + s_decrypt = decrypt; + s_callback_event_type = callback_event_type; + + s_time_read_started = CoreTiming::GetTicks(); + s_realtime_started_us = Common::Timer::GetTimeUs(); + + s_dvd_thread_start_working.Set(); + + CoreTiming::ScheduleEvent(ticks_until_completion, s_finish_read); +} + +static void FinishRead(u64 userdata, int cyclesLate) +{ + WaitUntilIdle(); + + DEBUG_LOG(DVDINTERFACE, "Disc has been read. Real time: %" PRIu64 " us. " + "Real time including delay: %" PRIu64 " us. Emulated time including delay: %" PRIu64 " us.", + s_realtime_done_us - s_realtime_started_us, + Common::Timer::GetTimeUs() - s_realtime_started_us, + (CoreTiming::GetTicks() - s_time_read_started) / (SystemTimers::GetTicksPerSecond() / 1000 / 1000)); + + if (s_dvd_success) + Memory::CopyToEmu(s_output_address, s_dvd_buffer.data(), s_length); + else + PanicAlertT("The disc could not be read (at 0x%" PRIx64 " - 0x%" PRIx64 ").", + s_dvd_offset, s_dvd_offset + s_length); + + // This will make the buffer take less space in savestates. + // Reducing the size doesn't change the amount of reserved memory, + // so this doesn't lead to extra memory allocations. + s_dvd_buffer.resize(0); + + // Notify the emulated software that the command has been executed + CoreTiming::ScheduleEvent_Immediate(s_callback_event_type, DVDInterface::INT_TCINT); +} + +static void DVDThread() +{ + Common::SetCurrentThreadName("DVD thread"); + + while (true) + { + s_dvd_thread_done_working.Set(); + + s_dvd_thread_start_working.Wait(); + + if (s_dvd_thread_exiting.IsSet()) + return; + + s_dvd_buffer.resize(s_length); + + s_dvd_success = DVDInterface::GetVolume().Read(s_dvd_offset, s_length, s_dvd_buffer.data(), s_decrypt); + + s_realtime_done_us = Common::Timer::GetTimeUs(); + } +} + +} diff --git a/Source/Core/Core/HW/DVDThread.h b/Source/Core/Core/HW/DVDThread.h new file mode 100644 index 0000000000..c2716ae7c8 --- /dev/null +++ b/Source/Core/Core/HW/DVDThread.h @@ -0,0 +1,21 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/ChunkFile.h" +#include "Common/CommonTypes.h" + +namespace DVDThread +{ + +void Start(); +void Stop(); +void DoState(PointerWrap &p); + +void WaitUntilIdle(); +void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt, + int callback_event_type, int ticks_until_completion); + +} diff --git a/Source/Core/Core/HW/Memmap.cpp b/Source/Core/Core/HW/Memmap.cpp index 9354fe94e0..467410d75b 100644 --- a/Source/Core/Core/HW/Memmap.cpp +++ b/Source/Core/Core/HW/Memmap.cpp @@ -247,8 +247,8 @@ bool AreMemoryBreakpointsActivated() static inline bool ValidCopyRange(u32 address, size_t size) { return (GetPointer(address) != nullptr && - GetPointer(address + u32(size)) != nullptr && - size < EXRAM_SIZE); // Make sure we don't have a range spanning seperate 2 banks + GetPointer(address + u32(size) - 1) != nullptr && + size < EXRAM_SIZE); // Make sure we don't have a range spanning 2 separate banks } void CopyFromEmu(void* data, u32 address, size_t size) diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 9a0711e6c2..c14c9dc9ef 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -68,7 +68,7 @@ static Common::Event g_compressAndDumpStateSyncEvent; static std::thread g_save_thread; // Don't forget to increase this after doing changes on the savestate system -static const u32 STATE_VERSION = 48; // Last changed in PR 3108 +static const u32 STATE_VERSION = 49; // Last changed in PR 2149 // Maps savestate versions to Dolphin versions. // Versions after 42 don't need to be added to this list,