diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index 93db9dda69..ce185e1ab0 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -239,7 +239,6 @@ static bool s_disc_inside = false; static bool s_stream = false; static bool s_stop_at_track_end = false; static CoreTiming::EventType* s_finish_executing_command; -static CoreTiming::EventType* s_dtk; static u64 s_last_read_offset; static u64 s_last_read_time; @@ -260,8 +259,9 @@ void GenerateDIInterrupt(DIInterruptType _DVDInterrupt); void WriteImmediate(u32 value, u32 output_address, bool reply_to_ios); bool ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, - bool decrypt, bool reply_to_ios, DIInterruptType* interrupt_type, - u64* ticks_until_completion); + bool decrypt, ReplyType reply_type, DIInterruptType* interrupt_type); + +u64 PackFinishExecutingCommandUserdata(ReplyType reply_type, DIInterruptType interrupt_type); u64 SimulateDiscReadTime(u64 offset, u32 length); s64 CalculateRawDiscReadTime(u64 offset, s64 length); @@ -355,7 +355,7 @@ static u32 ProcessDTKSamples(short* tempPCM, u32 num_samples) return samples_processed; } -static void DTKStreamingCallback(u64 userdata, s64 cyclesLate) +static void DTKStreamingCallback(const std::vector& audio_data, s64 cycles_late) { // Send audio to the mixer. static const int NUM_SAMPLES = 48000 / 2000 * 7; // 3.5ms of 48kHz samples @@ -373,7 +373,8 @@ static void DTKStreamingCallback(u64 userdata, s64 cyclesLate) g_sound_stream->GetMixer()->PushStreamingSamples(tempPCM, samples_processed); int ticks_to_dtk = int(SystemTimers::GetTicksPerSecond() * u64(samples_processed) / 48000); - CoreTiming::ScheduleEvent(ticks_to_dtk - cyclesLate, s_dtk); + u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::INT_TCINT); + CoreTiming::ScheduleEvent(ticks_to_dtk - cycles_late, s_finish_executing_command, userdata); } void Init() @@ -413,9 +414,9 @@ void Init() s_finish_executing_command = CoreTiming::RegisterEvent("FinishExecutingCommand", FinishExecutingCommandCallback); - s_dtk = CoreTiming::RegisterEvent("StreamingTimer", DTKStreamingCallback); - CoreTiming::ScheduleEvent(0, s_dtk); + u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::INT_TCINT); + CoreTiming::ScheduleEvent(0, s_finish_executing_command, userdata); } void Shutdown() @@ -647,8 +648,7 @@ void WriteImmediate(u32 value, u32 output_address, bool reply_to_ios) // 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, bool reply_to_ios, DIInterruptType* interrupt_type, - u64* ticks_until_completion) + bool decrypt, ReplyType reply_type, DIInterruptType* interrupt_type) { if (!s_disc_inside) { @@ -665,32 +665,34 @@ bool ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 if (DVD_length > output_length) { - WARN_LOG( - DVDINTERFACE, - "Detected attempt to read more data from the DVD than fit inside the out buffer. Clamp."); + WARN_LOG(DVDINTERFACE, "Detected an attempt to read more data from the DVD " + "than what fits inside the out buffer. Clamping."); DVD_length = output_length; } + u64 ticks_until_completion; if (SConfig::GetInstance().bFastDiscSpeed) + { // An optional hack to speed up loading times - *ticks_until_completion = + ticks_until_completion = output_length * (SystemTimers::GetTicksPerSecond() / BUFFER_TRANSFER_RATE); + } else - *ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length); + { + ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length); + } - DVDThread::StartRead(DVD_offset, output_address, DVD_length, decrypt, reply_to_ios, - (int)*ticks_until_completion); + DVDThread::StartReadToEmulatedRAM(output_address, DVD_offset, DVD_length, decrypt, reply_type, + ticks_until_completion); return true; } -// When the command has finished executing, callback_event_type -// will be called using CoreTiming::ScheduleEvent, -// with the userdata set to the interrupt type. void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address, u32 output_length, bool reply_to_ios) { + ReplyType reply_type = reply_to_ios ? ReplyType::IOS_HLE : ReplyType::Interrupt; DIInterruptType interrupt_type = INT_TCINT; - u64 ticks_until_completion = SystemTimers::GetTicksPerSecond() / 15000; + s64 ticks_until_completion = SystemTimers::GetTicksPerSecond() / 15000; bool command_handled_by_thread = false; // DVDLowRequestError needs access to the error code set by the previous command @@ -715,9 +717,8 @@ 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"); - command_handled_by_thread = - ExecuteReadCommand(0, output_address, 0x20, output_length, false, reply_to_ios, - &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand(0, output_address, 0x20, output_length, false, + reply_type, &interrupt_type); break; // Only used from WII_IPC. This is the only read command that decrypts data @@ -726,7 +727,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr command_1); command_handled_by_thread = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, true, - reply_to_ios, &interrupt_type, &ticks_until_completion); + reply_type, &interrupt_type); break; // Probably only used by Wii @@ -808,7 +809,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr { command_handled_by_thread = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, false, - reply_to_ios, &interrupt_type, &ticks_until_completion); + reply_type, &interrupt_type); } else { @@ -856,17 +857,15 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr ", DMABuffer = %08x, SrcLength = %08x, DMALength = %08x", iDVDOffset, output_address, command_2, output_length); - command_handled_by_thread = - ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, false, - reply_to_ios, &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand( + iDVDOffset, output_address, command_2, output_length, false, reply_type, &interrupt_type); } break; case 0x40: // Read DiscID INFO_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(output_address)); - command_handled_by_thread = - ExecuteReadCommand(0, output_address, 0x20, output_length, false, reply_to_ios, - &interrupt_type, &ticks_until_completion); + command_handled_by_thread = ExecuteReadCommand(0, output_address, 0x20, output_length, false, + reply_type, &interrupt_type); break; default: @@ -1083,33 +1082,52 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr // to simulate the speed of a real disc drive if (!command_handled_by_thread) { - u64 userdata = (static_cast(reply_to_ios) << 32) + static_cast(interrupt_type); - CoreTiming::ScheduleEvent((int)ticks_until_completion, s_finish_executing_command, userdata); + CoreTiming::ScheduleEvent(ticks_until_completion, s_finish_executing_command, + PackFinishExecutingCommandUserdata(reply_type, interrupt_type)); } } +u64 PackFinishExecutingCommandUserdata(ReplyType reply_type, DIInterruptType interrupt_type) +{ + return (static_cast(reply_type) << 32) + static_cast(interrupt_type); +} + void FinishExecutingCommandCallback(u64 userdata, s64 cycles_late) { - bool reply_to_ios = userdata >> 32 != 0; + ReplyType reply_type = static_cast(userdata >> 32); DIInterruptType interrupt_type = static_cast(userdata & 0xFFFFFFFF); - FinishExecutingCommand(reply_to_ios, interrupt_type); + FinishExecutingCommand(reply_type, interrupt_type, cycles_late); } -void FinishExecutingCommand(bool reply_to_ios, DIInterruptType interrupt_type) +void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type, s64 cycles_late, + const std::vector& data) { - if (reply_to_ios) + switch (reply_type) + { + case ReplyType::Interrupt: + { + if (s_DICR.TSTART) + { + s_DICR.TSTART = 0; + s_DILENGTH.Length = 0; + GenerateDIInterrupt(interrupt_type); + } + break; + } + + case ReplyType::IOS_HLE: { std::shared_ptr di = WII_IPC_HLE_Interface::GetDeviceByName("/dev/di"); if (di) std::static_pointer_cast(di)->FinishIOCtl(interrupt_type); - - // If di == nullptr, IOS was probably shut down, so the command shouldn't be completed + break; } - else if (s_DICR.TSTART) + + case ReplyType::DTK: { - s_DICR.TSTART = 0; - s_DILENGTH.Length = 0; - GenerateDIInterrupt(interrupt_type); + DTKStreamingCallback(data, cycles_late); + break; + } } } diff --git a/Source/Core/Core/HW/DVDInterface.h b/Source/Core/Core/HW/DVDInterface.h index e67e30ea9a..279dc15d92 100644 --- a/Source/Core/Core/HW/DVDInterface.h +++ b/Source/Core/Core/HW/DVDInterface.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "Common/CommonTypes.h" @@ -92,6 +93,13 @@ enum DIInterruptType : int INT_CVRINT = 3, }; +enum class ReplyType : u32 +{ + Interrupt, + IOS_HLE, + DTK +}; + void Init(); void Shutdown(); void DoState(PointerWrap& p); @@ -115,6 +123,7 @@ void ChangeDiscAsCPU(const std::string& new_path); // Can only be called by th bool ChangePartition(u64 offset); void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address, u32 output_length, bool reply_to_ios); -void FinishExecutingCommand(bool reply_to_ios, DIInterruptType interrupt_type); +void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type, s64 cycles_late, + const std::vector& data = std::vector()); } // end of namespace DVDInterface diff --git a/Source/Core/Core/HW/DVDThread.cpp b/Source/Core/Core/HW/DVDThread.cpp index 3a84cdc3c5..ad751aaac3 100644 --- a/Source/Core/Core/HW/DVDThread.cpp +++ b/Source/Core/Core/HW/DVDThread.cpp @@ -32,14 +32,16 @@ namespace DVDThread { struct ReadRequest { - u64 dvd_offset; + bool copy_to_ram; u32 output_address; + u64 dvd_offset; u32 length; bool decrypt; - // This determines which function will be used as a callback. - // We can't have a function pointer here, because they can't be in savestates. - bool reply_to_ios; + // This determines which code DVDInterface will run to reply + // to the emulated software. We can't use callbacks, + // because function pointers can't be stored in savestates. + DVDInterface::ReplyType reply_type; // IDs are used to uniquely identify a request. They must not be // identical to IDs of any other requests that currently exist, but @@ -56,6 +58,10 @@ using ReadResult = std::pair>; static void DVDThread(); +static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, + bool decrypt, DVDInterface::ReplyType reply_type, + s64 ticks_until_completion); + static void FinishRead(u64 id, s64 cycles_late); static CoreTiming::EventType* s_finish_read; @@ -143,18 +149,32 @@ void WaitUntilIdle() s_dvd_thread_done_working.Set(); } -void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt, bool reply_to_ios, - int ticks_until_completion) +void StartRead(u64 dvd_offset, u32 length, bool decrypt, DVDInterface::ReplyType reply_type, + s64 ticks_until_completion) +{ + StartReadInternal(false, 0, dvd_offset, length, decrypt, reply_type, ticks_until_completion); +} + +void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length, bool decrypt, + DVDInterface::ReplyType reply_type, s64 ticks_until_completion) +{ + StartReadInternal(true, output_address, dvd_offset, length, decrypt, reply_type, + ticks_until_completion); +} + +void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, + bool decrypt, DVDInterface::ReplyType reply_type, s64 ticks_until_completion) { _assert_(Core::IsCPUThread()); ReadRequest request; - request.dvd_offset = dvd_offset; + request.copy_to_ram = copy_to_ram; request.output_address = output_address; + request.dvd_offset = dvd_offset; request.length = length; request.decrypt = decrypt; - request.reply_to_ios = reply_to_ios; + request.reply_type = reply_type; u64 id = s_next_id++; request.id = id; @@ -213,14 +233,20 @@ static void FinishRead(u64 id, s64 cycles_late) (CoreTiming::GetTicks() - request.time_started_ticks) / (SystemTimers::GetTicksPerSecond() / 1000000)); - if (!buffer.empty()) - Memory::CopyToEmu(request.output_address, buffer.data(), request.length); - else + if (buffer.empty()) + { PanicAlertT("The disc could not be read (at 0x%" PRIx64 " - 0x%" PRIx64 ").", request.dvd_offset, request.dvd_offset + request.length); + } + else + { + if (request.copy_to_ram) + Memory::CopyToEmu(request.output_address, buffer.data(), request.length); + } // Notify the emulated software that the command has been executed - DVDInterface::FinishExecutingCommand(request.reply_to_ios, DVDInterface::INT_TCINT); + DVDInterface::FinishExecutingCommand(request.reply_type, DVDInterface::INT_TCINT, cycles_late, + buffer); } static void DVDThread() diff --git a/Source/Core/Core/HW/DVDThread.h b/Source/Core/Core/HW/DVDThread.h index b657e29f4f..c67a236d69 100644 --- a/Source/Core/Core/HW/DVDThread.h +++ b/Source/Core/Core/HW/DVDThread.h @@ -4,9 +4,14 @@ #pragma once -#include "Common/ChunkFile.h" #include "Common/CommonTypes.h" +class PointerWrap; +namespace DVDInterface +{ +enum class ReplyType : u32; +} + namespace DVDThread { void Start(); @@ -14,6 +19,8 @@ void Stop(); void DoState(PointerWrap& p); void WaitUntilIdle(); -void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt, bool reply_to_ios, - int ticks_until_completion); +void StartRead(u64 dvd_offset, u32 length, bool decrypt, DVDInterface::ReplyType reply_type, + s64 ticks_until_completion); +void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length, bool decrypt, + DVDInterface::ReplyType reply_type, s64 ticks_until_completion); }