From de86d539f2c437600ae13a3adafe9ad2f20e06a0 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 3 Jan 2015 10:28:57 +0100 Subject: [PATCH 1/5] DVDInterface: Move fast disc speed check to ExecuteReadCommand This breaks the workaround used for Star Wars: Rouge Leader, but that problem has gotten a more proper solution in 4.0-4889. --- Source/Core/Core/HW/DVDInterface.cpp | 20 +++++++------------ .../Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp | 3 --- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index 860c0ce4d4..be23b62d19 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -544,18 +544,9 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base) DVDCommandResult result = ExecuteCommand( m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex, m_DICMDBUF[2].Hex, m_DIMAR.Hex, m_DILENGTH.Hex, true); - if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed) - { - // Make sure fast disc speed performs "instant" reads; in addition - // to being used to speed up games, fast disc speed is used as a - // workaround for crashes in Star Wars Rogue Leader. - TransferComplete(result.interrupt_type, 0); - } - else - { - // The transfer is finished after a delay - CoreTiming::ScheduleEvent((int)result.ticks_until_completion, tc, result.interrupt_type); - } + + // The transfer is finished after a delay + CoreTiming::ScheduleEvent((int)result.ticks_until_completion, tc, result.interrupt_type); } }) ); @@ -621,7 +612,10 @@ DVDCommandResult ExecuteReadCommand(u64 DVD_offset, u32 output_address, } DVDCommandResult result; - result.ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length); + if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed) + result.ticks_until_completion = 0; // An optional hack to speed up loading times + else + result.ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length); if (!g_bDiscInside) { diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp index 9628afde9b..6592b54881 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp @@ -67,9 +67,6 @@ IPCCommandResult CWII_IPC_HLE_Device_di::IOCtl(u32 _CommandAddress) BufferOut, BufferOutSize, false); Memory::Write_U32(result.interrupt_type, _CommandAddress + 0x4); - if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed) - result.ticks_until_completion = 0; // An optional hack to speed up loading times - return { true, result.ticks_until_completion }; } From 443d371fa3eae499fc8c3736d69338c95f65c34c Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 3 Jan 2015 11:20:43 +0100 Subject: [PATCH 2/5] DVDInterface: Don't use return for ExecuteReadCommand result There's a small timing change here. If no disc is inserted, the ticks_until_completion passed to ExecuteReadCommand will be used. --- Source/Core/Core/HW/DVDInterface.cpp | 40 +++++++++++++--------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index be23b62d19..f9bab08896 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -257,8 +257,8 @@ void UpdateInterrupts(); void GenerateDIInterrupt(DIInterruptType _DVDInterrupt); void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF); -DVDCommandResult ExecuteReadCommand(u64 DVD_offset, u32 output_address, - u32 DVD_length, u32 output_length, bool decrypt); +void ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, + u32 output_length, bool decrypt, DVDCommandResult* result); u64 SimulateDiscReadTime(u64 offset, u32 length); s64 CalculateRawDiscReadTime(u64 offset, s64 length); @@ -602,33 +602,31 @@ void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF) Memory::Write_U32(value, output_address); } -DVDCommandResult ExecuteReadCommand(u64 DVD_offset, u32 output_address, - u32 DVD_length, u32 output_length, bool decrypt) +void ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, + u32 output_length, bool decrypt, DVDCommandResult* result) { + if (!g_bDiscInside) + { + g_ErrorCode = ERROR_NO_DISK | ERROR_COVER_H; + result->interrupt_type = INT_DEINT; + return; + } + if (DVD_length > output_length) { WARN_LOG(DVDINTERFACE, "Detected attempt to read more data from the DVD than fit inside the out buffer. Clamp."); DVD_length = output_length; } - DVDCommandResult result; if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed) - result.ticks_until_completion = 0; // An optional hack to speed up loading times + result->ticks_until_completion = 0; // An optional hack to speed up loading times else - result.ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length); - - if (!g_bDiscInside) - { - g_ErrorCode = ERROR_NO_DISK | ERROR_COVER_H; - result.interrupt_type = INT_DEINT; - return result; - } + result->ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length); if (!DVDRead(DVD_offset, output_address, DVD_length, decrypt)) PanicAlertT("Can't read from DVD_Plugin - DVD-Interface: Fatal Error"); - result.interrupt_type = INT_TCINT; - return result; + result->interrupt_type = INT_TCINT; } DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, @@ -681,13 +679,13 @@ DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, // Only seems to be used from WII_IPC, not through direct access case DVDLowReadDiskID: INFO_LOG(DVDINTERFACE, "DVDLowReadDiskID"); - result = ExecuteReadCommand(0, output_address, 0x20, output_length, false); + ExecuteReadCommand(0, output_address, 0x20, output_length, false, &result); 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); - result = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, true); + ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, true, &result); break; // Probably only used by Wii @@ -766,7 +764,7 @@ DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, (command_2 > 0x7ed40000 && command_2 < 0x7ed40008) || (((command_2 + command_1) > 0x7ed40000) && (command_2 + command_1) < 0x7ed40008))) { - result = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, false); + ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, false, &result); } else { @@ -862,13 +860,13 @@ DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, } } - result = ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, false); + ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, false, &result); } break; case 0x40: // Read DiscID INFO_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(output_address)); - result = ExecuteReadCommand(0, output_address, 0x20, output_length, false); + ExecuteReadCommand(0, output_address, 0x20, output_length, false, &result); break; default: From af4f872dfeb3a20a416b979545f2da7b8f8ca9ad Mon Sep 17 00:00:00 2001 From: JosJuice Date: Fri, 9 Jan 2015 14:19:25 +0100 Subject: [PATCH 3/5] DVDInterface: Schedule event in ExecuteCommand --- Source/Core/Core/HW/DVDInterface.cpp | 25 ++--- Source/Core/Core/HW/DVDInterface.h | 4 +- Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp | 9 +- Source/Core/Core/IPC_HLE/WII_IPC_HLE.h | 1 + .../Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp | 97 ++++++++++++++++--- .../Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.h | 17 ++-- 6 files changed, 118 insertions(+), 35 deletions(-) diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index f9bab08896..1058dcd25e 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -236,7 +236,7 @@ static u32 g_ErrorCode = 0; static bool g_bDiscInside = false; bool g_bStream = false; static bool g_bStopAtTrackEnd = false; -static int tc = 0; +static int finish_execute_command = 0; static int dtk = 0; static u64 g_last_read_offset; @@ -291,7 +291,7 @@ void DoState(PointerWrap &p) p.Do(g_bStopAtTrackEnd); } -static void TransferComplete(u64 userdata, int cyclesLate) +static void FinishExecuteCommand(u64 userdata, int cyclesLate) { if (m_DICR.TSTART) { @@ -393,7 +393,7 @@ void Init() ejectDisc = CoreTiming::RegisterEvent("EjectDisc", EjectDiscCallback); insertDisc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback); - tc = CoreTiming::RegisterEvent("TransferComplete", TransferComplete); + finish_execute_command = CoreTiming::RegisterEvent("FinishExecuteCommand", FinishExecuteCommand); dtk = CoreTiming::RegisterEvent("StreamingTimer", DTKStreamingCallback); CoreTiming::ScheduleEvent(0, dtk); @@ -541,12 +541,8 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base) m_DICR.Hex = val & 7; if (m_DICR.TSTART) { - DVDCommandResult result = ExecuteCommand( - m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex, m_DICMDBUF[2].Hex, - m_DIMAR.Hex, m_DILENGTH.Hex, true); - - // The transfer is finished after a delay - CoreTiming::ScheduleEvent((int)result.ticks_until_completion, tc, result.interrupt_type); + ExecuteCommand(m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex, m_DICMDBUF[2].Hex, + m_DIMAR.Hex, m_DILENGTH.Hex, true, finish_execute_command); } }) ); @@ -629,8 +625,11 @@ void ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, result->interrupt_type = INT_TCINT; } -DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, - u32 output_address, u32 output_length, bool write_to_DIIMMBUF) +// 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 write_to_DIIMMBUF, int callback_event_type) { DVDCommandResult result; result.interrupt_type = INT_TCINT; @@ -1201,7 +1200,9 @@ DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, break; } - return result; + // The command will finish executing after a delay, + // to simulate the speed of a real disc drive + CoreTiming::ScheduleEvent((int)result.ticks_until_completion, callback_event_type, result.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 5b8a249fee..094896dd36 100644 --- a/Source/Core/Core/HW/DVDInterface.h +++ b/Source/Core/Core/HW/DVDInterface.h @@ -105,7 +105,7 @@ void ChangeDisc(const std::string& fileName); // DVD Access Functions bool DVDRead(u64 _iDVDOffset, u32 _iRamAddress, u32 _iLength, bool decrypt); extern bool g_bStream; -DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, - u32 output_address, u32 output_length, bool write_to_DIIMMBUF); +void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address, u32 output_length, + bool write_to_DIIMMBUF, int callback_event_type); } // end of namespace DVDInterface diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp b/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp index 2f5de7e188..08b5ed2ed9 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp @@ -85,7 +85,7 @@ static u64 last_reply_time; static const u64 ENQUEUE_REQUEST_FLAG = 0x100000000ULL; static const u64 ENQUEUE_ACKNOWLEDGEMENT_FLAG = 0x200000000ULL; -static void EnqueueEventCallback(u64 userdata, int) +static void EnqueueEvent(u64 userdata, int cycles_late = 0) { if (userdata & ENQUEUE_ACKNOWLEDGEMENT_FLAG) { @@ -144,7 +144,7 @@ void Init() g_DeviceMap[i] = new CWII_IPC_HLE_Device_stub(i, "/dev/usb/oh1"); i++; g_DeviceMap[i] = new IWII_IPC_HLE_Device(i, "_Unimplemented_Device_"); i++; - event_enqueue = CoreTiming::RegisterEvent("IPCEvent", EnqueueEventCallback); + event_enqueue = CoreTiming::RegisterEvent("IPCEvent", EnqueueEvent); } void Reset(bool _bHard) @@ -563,6 +563,11 @@ void EnqueueReply_Threadsafe(u32 address, int cycles_in_future) CoreTiming::ScheduleEvent_Threadsafe(cycles_in_future, event_enqueue, address); } +void EnqueueReply_Immediate(u32 address) +{ + EnqueueEvent(address); +} + void EnqueueCommandAcknowledgement(u32 address, int cycles_in_future) { CoreTiming::ScheduleEvent(cycles_in_future, event_enqueue, diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE.h b/Source/Core/Core/IPC_HLE/WII_IPC_HLE.h index 1175462dc3..3383642b74 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE.h +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE.h @@ -80,6 +80,7 @@ void ExecuteCommand(u32 _Address); void EnqueueRequest(u32 address); void EnqueueReply(u32 address, int cycles_in_future = 0); void EnqueueReply_Threadsafe(u32 address, int cycles_in_future = 0); +void EnqueueReply_Immediate(u32 address); void EnqueueCommandAcknowledgement(u32 _Address, int cycles_in_future = 0); } // end of namespace WII_IPC_HLE_Interface diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp index 6592b54881..07a22b79a2 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp @@ -9,6 +9,7 @@ #include "Common/Logging/LogManager.h" #include "Core/ConfigManager.h" +#include "Core/CoreTiming.h" #include "Core/VolumeHandler.h" #include "Core/HW/DVDInterface.h" #include "Core/HW/Memmap.h" @@ -17,14 +18,38 @@ #include "Core/IPC_HLE/WII_IPC_HLE.h" #include "Core/IPC_HLE/WII_IPC_HLE_Device_DI.h" -using namespace DVDInterface; +static CWII_IPC_HLE_Device_di* g_di_pointer; +static int ioctl_callback; -CWII_IPC_HLE_Device_di::CWII_IPC_HLE_Device_di(u32 _DeviceID, const std::string& _rDeviceName ) +static void IOCtlCallback(u64 userdata, int cycles_late) +{ + if (g_di_pointer != nullptr) + g_di_pointer->FinishIOCtl((DVDInterface::DIInterruptType)userdata); + + // If g_di_pointer == nullptr, IOS was probably shut down, + // so the command shouldn't be completed +} + +CWII_IPC_HLE_Device_di::CWII_IPC_HLE_Device_di(u32 _DeviceID, const std::string& _rDeviceName) : IWII_IPC_HLE_Device(_DeviceID, _rDeviceName) -{} +{ + if (g_di_pointer == nullptr) + ERROR_LOG(WII_IPC_DVD, "Trying to run two DI devices at once. IOCtl may not behave as expected."); + + g_di_pointer = this; + ioctl_callback = CoreTiming::RegisterEvent("IOCtlCallbackDI", IOCtlCallback); +} CWII_IPC_HLE_Device_di::~CWII_IPC_HLE_Device_di() -{} +{ + g_di_pointer = nullptr; +} + +void CWII_IPC_HLE_Device_di::DoState(PointerWrap& p) +{ + DoStateShared(p); + p.Do(m_commands_to_execute); +} IPCCommandResult CWII_IPC_HLE_Device_di::Open(u32 _CommandAddress, u32 _Mode) { @@ -43,10 +68,29 @@ IPCCommandResult CWII_IPC_HLE_Device_di::Close(u32 _CommandAddress, bool _bForce IPCCommandResult CWII_IPC_HLE_Device_di::IOCtl(u32 _CommandAddress) { - u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10); - u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14); - u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18); - u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C); + // DI IOCtls are handled in a special way by Dolphin + // compared to other WII_IPC_HLE functions. + // This is a wrapper around DVDInterface's ExecuteCommand, + // which will execute commands more or less asynchronously. + // Only one command can be executed at a time, so commands + // are queued until DVDInterface is ready to handle them. + + bool ready_to_execute = m_commands_to_execute.empty(); + m_commands_to_execute.push_back(_CommandAddress); + if (ready_to_execute) + StartIOCtl(_CommandAddress); + + // DVDInterface handles the timing, and we handle the reply, + // so WII_IPC_HLE shouldn't do any of that. + return IPC_NO_REPLY; +} + +void CWII_IPC_HLE_Device_di::StartIOCtl(u32 command_address) +{ + u32 BufferIn = Memory::Read_U32(command_address + 0x10); + u32 BufferInSize = Memory::Read_U32(command_address + 0x14); + u32 BufferOut = Memory::Read_U32(command_address + 0x18); + u32 BufferOutSize = Memory::Read_U32(command_address + 0x1C); u32 command_0 = Memory::Read_U32(BufferIn); u32 command_1 = Memory::Read_U32(BufferIn + 4); @@ -63,11 +107,38 @@ IPCCommandResult CWII_IPC_HLE_Device_di::IOCtl(u32 _CommandAddress) Memory::Memset(BufferOut, 0, BufferOutSize); } - DVDCommandResult result = ExecuteCommand(command_0, command_1, command_2, - BufferOut, BufferOutSize, false); - Memory::Write_U32(result.interrupt_type, _CommandAddress + 0x4); + // DVDInterface's ExecuteCommand handles most of the work. + // The IOCtl callback is used to generate a reply afterwards. + DVDInterface::ExecuteCommand(command_0, command_1, command_2, BufferOut, BufferOutSize, + false, ioctl_callback); +} - return { true, result.ticks_until_completion }; +void CWII_IPC_HLE_Device_di::FinishIOCtl(DVDInterface::DIInterruptType interrupt_type) +{ + if (m_commands_to_execute.empty()) + { + PanicAlertT("WII_IPC_HLE_Device_DI tried to reply to non-existing command"); + return; + } + + // This command has been executed, so it's removed from the queue + u32 command_address = m_commands_to_execute.front(); + m_commands_to_execute.pop_front(); + + // The DI interrupt type is used as a return value + Memory::Write_U32(interrupt_type, command_address + 4); + + // The original hardware overwrites the command type with the async reply type. + Memory::Write_U32(IPC_REP_ASYNC, command_address); + // IOS also seems to write back the command that was responded to in the FD field. + Memory::Write_U32(Memory::Read_U32(command_address), command_address + 8); + // Generate a reply to the IPC command + WII_IPC_HLE_Interface::EnqueueReply_Immediate(command_address); + + // DVDInterface is now ready to execute another command, + // so we start executing a command from the queue if there is one + if (!m_commands_to_execute.empty()) + StartIOCtl(m_commands_to_execute.front()); } IPCCommandResult CWII_IPC_HLE_Device_di::IOCtlV(u32 _CommandAddress) @@ -85,7 +156,7 @@ IPCCommandResult CWII_IPC_HLE_Device_di::IOCtlV(u32 _CommandAddress) u32 ReturnValue = 0; switch (CommandBuffer.Parameter) { - case DVDLowOpenPartition: + case DVDInterface::DVDLowOpenPartition: { _dbg_assert_msg_(WII_IPC_DVD, CommandBuffer.InBuffer[1].m_Address == 0, "DVDLowOpenPartition with ticket"); _dbg_assert_msg_(WII_IPC_DVD, CommandBuffer.InBuffer[2].m_Address == 0, "DVDLowOpenPartition with cert chain"); diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.h b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.h index 6c10e8f258..4ca874ed1d 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.h +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.h @@ -4,14 +4,10 @@ #pragma once +#include +#include "Core/HW/DVDInterface.h" #include "Core/IPC_HLE/WII_IPC_HLE_Device.h" -namespace DiscIO -{ - class IVolume; - class IFileSystem; -} - class CWII_IPC_HLE_Device_di : public IWII_IPC_HLE_Device { public: @@ -20,9 +16,18 @@ public: virtual ~CWII_IPC_HLE_Device_di(); + void DoState(PointerWrap& p) override; + IPCCommandResult Open(u32 _CommandAddress, u32 _Mode) override; IPCCommandResult Close(u32 _CommandAddress, bool _bForce) override; IPCCommandResult IOCtl(u32 _CommandAddress) override; IPCCommandResult IOCtlV(u32 _CommandAddress) override; + + void FinishIOCtl(DVDInterface::DIInterruptType interrupt_type); +private: + + void StartIOCtl(u32 command_address); + + std::deque m_commands_to_execute; }; From 3414f818cda95925c520d52151f91cb1fdfd46f5 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Wed, 7 Jan 2015 17:55:21 +0100 Subject: [PATCH 4/5] DVDInterface: Get rid of DVDCommandResult struct It's only used in one place and is no longer necesssary. --- Source/Core/Core/HW/DVDInterface.cpp | 44 +++++++++++++++------------- Source/Core/Core/HW/DVDInterface.h | 6 ---- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index 1058dcd25e..7510651dd2 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -257,8 +257,8 @@ void UpdateInterrupts(); void GenerateDIInterrupt(DIInterruptType _DVDInterrupt); void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF); -void ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, - u32 output_length, bool decrypt, DVDCommandResult* result); +void ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, + bool decrypt, DIInterruptType* interrupt_type, u64* ticks_until_completion); u64 SimulateDiscReadTime(u64 offset, u32 length); s64 CalculateRawDiscReadTime(u64 offset, s64 length); @@ -598,13 +598,13 @@ void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF) Memory::Write_U32(value, output_address); } -void ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, - u32 output_length, bool decrypt, DVDCommandResult* result) +void ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, + bool decrypt, DIInterruptType* interrupt_type, u64* ticks_until_completion) { if (!g_bDiscInside) { g_ErrorCode = ERROR_NO_DISK | ERROR_COVER_H; - result->interrupt_type = INT_DEINT; + *interrupt_type = INT_DEINT; return; } @@ -615,14 +615,14 @@ void ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, } if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed) - result->ticks_until_completion = 0; // An optional hack to speed up loading times + *ticks_until_completion = 0; // An optional hack to speed up loading times else - result->ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length); + *ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length); if (!DVDRead(DVD_offset, output_address, DVD_length, decrypt)) PanicAlertT("Can't read from DVD_Plugin - DVD-Interface: Fatal Error"); - result->interrupt_type = INT_TCINT; + *interrupt_type = INT_TCINT; } // When the command has finished executing, callback_event_type @@ -631,9 +631,8 @@ void ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address, u32 output_length, bool write_to_DIIMMBUF, int callback_event_type) { - DVDCommandResult result; - result.interrupt_type = INT_TCINT; - result.ticks_until_completion = SystemTimers::GetTicksPerSecond() / 15000; + DIInterruptType interrupt_type = INT_TCINT; + u64 ticks_until_completion = SystemTimers::GetTicksPerSecond() / 15000; bool GCAM = (SConfig::GetInstance().m_SIDevice[0] == SIDEVICE_AM_BASEBOARD) && (SConfig::GetInstance().m_EXIDevice[2] == EXIDEVICE_AM_BASEBOARD); @@ -678,19 +677,21 @@ 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"); - ExecuteReadCommand(0, output_address, 0x20, output_length, false, &result); + ExecuteReadCommand(0, output_address, 0x20, output_length, + false, &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); - ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, true, &result); + ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, + true, &interrupt_type, &ticks_until_completion); break; // Probably only used by Wii case DVDLowWaitForCoverClose: INFO_LOG(DVDINTERFACE, "DVDLowWaitForCoverClose"); - result.interrupt_type = (DIInterruptType)4; // ??? + interrupt_type = (DIInterruptType)4; // ??? break; // "Set Extension"...not sure what it does. GC only? @@ -763,14 +764,15 @@ 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))) { - ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, false, &result); + ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, + false, &interrupt_type, &ticks_until_completion); } else { WARN_LOG(DVDINTERFACE, "DVDLowUnencryptedRead: trying to read out of bounds @ %09" PRIx64, (u64)command_2 << 2); g_ErrorCode = ERROR_READY | ERROR_BLOCK_OOB; // Should cause software to call DVDLowRequestError - result.interrupt_type = INT_BRKINT; + interrupt_type = INT_BRKINT; } break; @@ -795,7 +797,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr // Does not work on retail discs/drives // Retail games send this command to see if they are running on real retail hw g_ErrorCode = ERROR_READY | ERROR_INV_CMD; - result.interrupt_type = INT_BRKINT; + interrupt_type = INT_BRKINT; break; // DMA Read from Disc. Only seems to be used through direct access, not WII_IPC @@ -859,13 +861,15 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr } } - ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, false, &result); + ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, + false, &interrupt_type, &ticks_until_completion); } break; case 0x40: // Read DiscID INFO_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(output_address)); - ExecuteReadCommand(0, output_address, 0x20, output_length, false, &result); + ExecuteReadCommand(0, output_address, 0x20, output_length, + false, &interrupt_type, &ticks_until_completion); break; default: @@ -1202,7 +1206,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr // The command will finish executing after a delay, // to simulate the speed of a real disc drive - CoreTiming::ScheduleEvent((int)result.ticks_until_completion, callback_event_type, result.interrupt_type); + 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 094896dd36..3f9337a9e6 100644 --- a/Source/Core/Core/HW/DVDInterface.h +++ b/Source/Core/Core/HW/DVDInterface.h @@ -85,12 +85,6 @@ enum DIInterruptType INT_CVRINT = 3, }; -struct DVDCommandResult -{ - DIInterruptType interrupt_type; - u64 ticks_until_completion; -}; - void Init(); void Shutdown(); void DoState(PointerWrap &p); From 3ac0977bc6649fc451b7a320eeff8b128d52640b Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 17 Feb 2015 14:57:36 +0100 Subject: [PATCH 5/5] DVDInterface: Read disc after delay, not before --- Source/Core/Core/CoreTiming.cpp | 8 +- Source/Core/Core/CoreTiming.h | 10 +-- Source/Core/Core/HW/DVDInterface.cpp | 110 ++++++++++++++++++++++----- Source/Core/Core/State.cpp | 2 +- 4 files changed, 104 insertions(+), 26 deletions(-) diff --git a/Source/Core/Core/CoreTiming.cpp b/Source/Core/Core/CoreTiming.cpp index 2a7e571dc2..c4c1cac708 100644 --- a/Source/Core/Core/CoreTiming.cpp +++ b/Source/Core/Core/CoreTiming.cpp @@ -232,8 +232,14 @@ void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata tsQueue.Push(ne); } +// Executes an event immediately, then returns. +void ScheduleEvent_Immediate(int event_type, u64 userdata) +{ + event_types[event_type].callback(userdata, 0); +} + // Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the CPU thread -// in which case the event will get handled immediately, before returning. +// in which case this is the same as ScheduleEvent_Immediate. void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata) { if (Core::IsCPUThread()) diff --git a/Source/Core/Core/CoreTiming.h b/Source/Core/Core/CoreTiming.h index 1de5b92cec..fdd5fe14eb 100644 --- a/Source/Core/Core/CoreTiming.h +++ b/Source/Core/Core/CoreTiming.h @@ -43,11 +43,11 @@ void DoState(PointerWrap &p); int RegisterEvent(const std::string& name, TimedCallback callback); void UnregisterAllEvents(); -// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk, -// when we implement state saves. -void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata=0); -void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata=0); -void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata=0); +// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from savestates. +void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata = 0); +void ScheduleEvent_Immediate(int event_type, u64 userdata = 0); +void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata = 0); +void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata = 0); // We only permit one event of each type in the queue at a time. void RemoveEvent(int event_type); diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index 7510651dd2..e72509022a 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -213,6 +213,22 @@ 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; +}; + // STATE_TO_SAVE // hardware registers @@ -225,6 +241,8 @@ 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; @@ -237,6 +255,7 @@ 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; @@ -257,8 +276,8 @@ void UpdateInterrupts(); void GenerateDIInterrupt(DIInterruptType _DVDInterrupt); void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF); -void ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, - bool decrypt, DIInterruptType* interrupt_type, u64* ticks_until_completion); +DVDReadCommand ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, + bool decrypt, DIInterruptType* interrupt_type, u64* ticks_until_completion); u64 SimulateDiscReadTime(u64 offset, u32 length); s64 CalculateRawDiscReadTime(u64 offset, s64 length); @@ -274,6 +293,8 @@ 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); @@ -301,6 +322,30 @@ static void FinishExecuteCommand(u64 userdata, int cyclesLate) } } +static void FinishExecuteReadCommand(u64 userdata, int cyclesLate) +{ + if (!current_read_command.is_valid) + { + PanicAlertT("DVDInterface tried to execute non-existing command"); + } + 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) { u32 samples_processed = 0; @@ -376,6 +421,8 @@ 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; @@ -394,6 +441,7 @@ 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); @@ -598,14 +646,19 @@ void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF) Memory::Write_U32(value, output_address); } -void ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, - bool decrypt, DIInterruptType* interrupt_type, u64* ticks_until_completion) +// 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) { + DVDReadCommand command; + if (!g_bDiscInside) { g_ErrorCode = ERROR_NO_DISK | ERROR_COVER_H; *interrupt_type = INT_DEINT; - return; + command.is_valid = false; + return command; } if (DVD_length > output_length) @@ -619,10 +672,13 @@ void ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 else *ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length); - if (!DVDRead(DVD_offset, output_address, DVD_length, decrypt)) - PanicAlertT("Can't read from DVD_Plugin - DVD-Interface: Fatal Error"); - *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; } // When the command has finished executing, callback_event_type @@ -633,6 +689,8 @@ 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 GCAM = (SConfig::GetInstance().m_SIDevice[0] == SIDEVICE_AM_BASEBOARD) && (SConfig::GetInstance().m_EXIDevice[2] == EXIDEVICE_AM_BASEBOARD); @@ -677,15 +735,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"); - ExecuteReadCommand(0, output_address, 0x20, output_length, - false, &interrupt_type, &ticks_until_completion); + read_command = ExecuteReadCommand(0, output_address, 0x20, output_length, + false, &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); - ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, - true, &interrupt_type, &ticks_until_completion); + read_command = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, + true, &interrupt_type, &ticks_until_completion); break; // Probably only used by Wii @@ -764,8 +822,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))) { - ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, - false, &interrupt_type, &ticks_until_completion); + read_command = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, + false, &interrupt_type, &ticks_until_completion); } else { @@ -861,15 +919,15 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr } } - ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, - false, &interrupt_type, &ticks_until_completion); + read_command = ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, + false, &interrupt_type, &ticks_until_completion); } break; case 0x40: // Read DiscID INFO_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(output_address)); - ExecuteReadCommand(0, output_address, 0x20, output_length, - false, &interrupt_type, &ticks_until_completion); + read_command = ExecuteReadCommand(0, output_address, 0x20, output_length, + false, &interrupt_type, &ticks_until_completion); break; default: @@ -1206,7 +1264,21 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr // The command will finish executing after a delay, // to simulate the speed of a real disc drive - CoreTiming::ScheduleEvent((int)ticks_until_completion, callback_event_type, interrupt_type); + 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 + { + 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/State.cpp b/Source/Core/Core/State.cpp index 2b3ce95e08..378dcde7c0 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -64,7 +64,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 = 39; +static const u32 STATE_VERSION = 40; enum {