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..a60c21f7b9 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,30 +312,6 @@ 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)
{
u32 samples_processed = 0;
@@ -417,6 +375,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 +389,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 +407,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 +414,7 @@ void Init()
void Shutdown()
{
+ DVDThread::Stop();
s_inserted_volume.reset();
}
@@ -687,19 +645,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 +674,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 +687,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 +732,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 +819,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 +916,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 +1259,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/DVDThread.cpp b/Source/Core/Core/HW/DVDThread.cpp
new file mode 100644
index 0000000000..1182b6c1ee
--- /dev/null
+++ b/Source/Core/Core/HW/DVDThread.cpp
@@ -0,0 +1,75 @@
+// Copyright 2015 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include
+
+#include "Common/ChunkFile.h"
+#include "Common/CommonTypes.h"
+#include "Common/MsgHandler.h"
+
+#include "Core/CoreTiming.h"
+#include "Core/HW/DVDInterface.h"
+#include "Core/HW/DVDThread.h"
+
+#include "DiscIO/Volume.h"
+
+namespace DVDThread
+{
+
+static void FinishRead(u64 userdata, int cyclesLate);
+static int s_finish_read;
+
+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;
+
+void Start()
+{
+ s_finish_read = CoreTiming::RegisterEvent("FinishReadDVDThread", FinishRead);
+}
+
+void Stop()
+{
+
+}
+
+void DoState(PointerWrap &p)
+{
+ p.Do(s_dvd_offset);
+ p.Do(s_output_address);
+ p.Do(s_length);
+ p.Do(s_decrypt);
+ p.Do(s_callback_event_type);
+}
+
+void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt,
+ int callback_event_type, int ticks_until_completion)
+{
+ s_dvd_offset = dvd_offset;
+ s_output_address = output_address;
+ s_length = length;
+ s_decrypt = decrypt;
+ s_callback_event_type = callback_event_type;
+ CoreTiming::ScheduleEvent(ticks_until_completion, s_finish_read);
+}
+
+static void FinishRead(u64 userdata, int cyclesLate)
+{
+ // Here is the actual disc reading
+ if (!DVDInterface::DVDRead(s_dvd_offset, s_output_address, s_length, s_decrypt))
+ {
+ PanicAlertT("The disc could not be read (at 0x%" PRIx64 " - 0x%" PRIx64 ").",
+ s_dvd_offset, s_dvd_offset + s_length);
+ }
+
+ // Notify the emulated software that the command has been executed
+ CoreTiming::ScheduleEvent_Immediate(s_callback_event_type, DVDInterface::INT_TCINT);
+}
+
+}
diff --git a/Source/Core/Core/HW/DVDThread.h b/Source/Core/Core/HW/DVDThread.h
new file mode 100644
index 0000000000..0aa5f296cb
--- /dev/null
+++ b/Source/Core/Core/HW/DVDThread.h
@@ -0,0 +1,20 @@
+// 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 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/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,