DVDInterface/DVDThread: Don't store CoreTiming event IDs in savestates

CoreTiming event types aren't guaranteed to be stable across
runs of Dolphin, so they shouldn't be in savestates.
This commit is contained in:
JosJuice 2016-03-30 21:45:56 +02:00
parent 2063fc671a
commit be191c0473
6 changed files with 74 additions and 65 deletions

View File

@ -25,6 +25,8 @@
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/StreamADPCM.h"
#include "Core/HW/SystemTimers.h"
#include "Core/IPC_HLE/WII_IPC_HLE.h"
#include "Core/IPC_HLE/WII_IPC_HLE_Device_DI.h"
#include "DiscIO/Volume.h"
#include "DiscIO/VolumeCreator.h"
@ -244,7 +246,7 @@ static u32 s_error_code = 0;
static bool s_disc_inside = false;
static bool s_stream = false;
static bool s_stop_at_track_end = false;
static int s_finish_execute_command = 0;
static int s_finish_executing_command = 0;
static int s_dtk = 0;
static u64 s_last_read_offset;
@ -258,15 +260,16 @@ static int s_insert_disc;
static void EjectDiscCallback(u64 userdata, s64 cyclesLate);
static void InsertDiscCallback(u64 userdata, s64 cyclesLate);
static void FinishExecutingCommandCallback(u64 userdata, s64 cycles_late);
void SetLidOpen(bool _bOpen);
void UpdateInterrupts();
void GenerateDIInterrupt(DIInterruptType _DVDInterrupt);
void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF);
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,
int callback_event_type, DIInterruptType* interrupt_type, u64* ticks_until_completion);
bool reply_to_ios, DIInterruptType* interrupt_type, u64* ticks_until_completion);
u64 SimulateDiscReadTime(u64 offset, u32 length);
s64 CalculateRawDiscReadTime(u64 offset, s64 length);
@ -301,16 +304,6 @@ void DoState(PointerWrap &p)
DVDThread::DoState(p);
}
static void FinishExecuteCommand(u64 userdata, s64 cyclesLate)
{
if (s_DICR.TSTART)
{
s_DICR.TSTART = 0;
s_DILENGTH.Length = 0;
GenerateDIInterrupt((DIInterruptType)userdata);
}
}
static u32 ProcessDTKSamples(short *tempPCM, u32 num_samples)
{
// TODO: Read audio data using the DVD thread instead of blocking on it?
@ -408,7 +401,7 @@ void Init()
s_eject_disc = CoreTiming::RegisterEvent("EjectDisc", EjectDiscCallback);
s_insert_disc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback);
s_finish_execute_command = CoreTiming::RegisterEvent("FinishExecuteCommand", FinishExecuteCommand);
s_finish_executing_command = CoreTiming::RegisterEvent("FinishExecutingCommand", FinishExecutingCommandCallback);
s_dtk = CoreTiming::RegisterEvent("StreamingTimer", DTKStreamingCallback);
CoreTiming::ScheduleEvent(0, s_dtk);
@ -589,7 +582,7 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
if (s_DICR.TSTART)
{
ExecuteCommand(s_DICMDBUF[0].Hex, s_DICMDBUF[1].Hex, s_DICMDBUF[2].Hex,
s_DIMAR.Hex, s_DILENGTH.Hex, true, s_finish_execute_command);
s_DIMAR.Hex, s_DILENGTH.Hex, false);
}
})
);
@ -637,17 +630,17 @@ void GenerateDIInterrupt(DIInterruptType dvd_interrupt)
UpdateInterrupts();
}
void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF)
void WriteImmediate(u32 value, u32 output_address, bool reply_to_ios)
{
if (write_to_DIIMMBUF)
s_DIIMMBUF.Hex = value;
else
if (reply_to_ios)
Memory::Write_U32(value, output_address);
else
s_DIIMMBUF.Hex = value;
}
// 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)
bool reply_to_ios, DIInterruptType* interrupt_type, u64* ticks_until_completion)
{
if (!s_disc_inside)
{
@ -675,15 +668,15 @@ bool ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32
*ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length);
DVDThread::StartRead(DVD_offset, output_address, DVD_length, decrypt,
callback_event_type, (int)*ticks_until_completion);
reply_to_ios, (int)*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 write_to_DIIMMBUF, int callback_event_type)
void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address,
u32 output_length, bool reply_to_ios)
{
DIInterruptType interrupt_type = INT_TCINT;
u64 ticks_until_completion = SystemTimers::GetTicksPerSecond() / 15000;
@ -713,7 +706,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr
{
// 0x29484100...
// was 21 i'm not entirely sure about this, but it works well.
WriteImmediate(0x21000000, output_address, write_to_DIIMMBUF);
WriteImmediate(0x21000000, output_address, reply_to_ios);
}
else
{
@ -733,14 +726,14 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr
case DVDLowReadDiskID:
INFO_LOG(DVDINTERFACE, "DVDLowReadDiskID");
command_handled_by_thread = ExecuteReadCommand(0, output_address, 0x20, output_length, false,
callback_event_type, &interrupt_type, &ticks_until_completion);
reply_to_ios, &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);
command_handled_by_thread = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, true,
callback_event_type, &interrupt_type, &ticks_until_completion);
reply_to_ios, &interrupt_type, &ticks_until_completion);
break;
// Probably only used by Wii
@ -756,7 +749,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr
// Probably only used though WII_IPC
case DVDLowGetCoverReg:
WriteImmediate(s_DICVR.Hex, output_address, write_to_DIIMMBUF);
WriteImmediate(s_DICVR.Hex, output_address, reply_to_ios);
INFO_LOG(DVDINTERFACE, "DVDLowGetCoverReg 0x%08x", s_DICVR.Hex);
break;
@ -789,7 +782,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr
// Probably only used by Wii
case DVDLowGetCoverStatus:
WriteImmediate(s_disc_inside ? 2 : 1, output_address, write_to_DIIMMBUF);
WriteImmediate(s_disc_inside ? 2 : 1, output_address, reply_to_ios);
INFO_LOG(DVDINTERFACE, "DVDLowGetCoverStatus: Disc %sInserted", s_disc_inside ? "" : "Not ");
break;
@ -820,7 +813,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr
(((command_2 + command_1) > 0x7ed40000) && (command_2 + command_1) < 0x7ed40008)))
{
command_handled_by_thread = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, false,
callback_event_type, &interrupt_type, &ticks_until_completion);
reply_to_ios, &interrupt_type, &ticks_until_completion);
}
else
{
@ -917,14 +910,14 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr
}
command_handled_by_thread = ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, false,
callback_event_type, &interrupt_type, &ticks_until_completion);
reply_to_ios, &interrupt_type, &ticks_until_completion);
}
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,
callback_event_type, &interrupt_type, &ticks_until_completion);
reply_to_ios, &interrupt_type, &ticks_until_completion);
break;
default:
@ -1060,7 +1053,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr
break;
}
memset(s_media_buffer + 0x20, 0, 0x20);
WriteImmediate(0x66556677, output_address, write_to_DIIMMBUF); // just a random value that works.
WriteImmediate(0x66556677, output_address, reply_to_ios); // just a random value that works.
}
break;
@ -1105,7 +1098,7 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr
// Used by both GC and Wii
case DVDLowRequestError:
INFO_LOG(DVDINTERFACE, "Requesting error... (0x%08x)", s_error_code);
WriteImmediate(s_error_code, output_address, write_to_DIIMMBUF);
WriteImmediate(s_error_code, output_address, reply_to_ios);
s_error_code = 0;
break;
@ -1162,19 +1155,19 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr
case 0x00: // Returns streaming status
INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status AudioPos:%08x/%08x CurrentStart:%08x CurrentLength:%08x",
s_audio_position, s_current_start + s_current_length, s_current_start, s_current_length);
WriteImmediate(s_stream ? 1 : 0, output_address, write_to_DIIMMBUF);
WriteImmediate(s_stream ? 1 : 0, output_address, reply_to_ios);
break;
case 0x01: // Returns the current offset
INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status AudioPos:%08x", s_audio_position);
WriteImmediate(s_audio_position >> 2, output_address, write_to_DIIMMBUF);
WriteImmediate(s_audio_position >> 2, output_address, reply_to_ios);
break;
case 0x02: // Returns the start offset
INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status CurrentStart:%08x", s_current_start);
WriteImmediate(s_current_start >> 2, output_address, write_to_DIIMMBUF);
WriteImmediate(s_current_start >> 2, output_address, reply_to_ios);
break;
case 0x03: // Returns the total length
INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status CurrentLength:%08x", s_current_length);
WriteImmediate(s_current_length >> 2, output_address, write_to_DIIMMBUF);
WriteImmediate(s_current_length >> 2, output_address, reply_to_ios);
break;
default:
WARN_LOG(DVDINTERFACE, "(Audio): Subcommand: %02x Request Audio status %s", command_0 >> 16 & 0xFF, s_stream ? "on" : "off");
@ -1263,7 +1256,35 @@ 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
if (!command_handled_by_thread)
CoreTiming::ScheduleEvent((int)ticks_until_completion, callback_event_type, interrupt_type);
{
u64 userdata = (static_cast<u64>(reply_to_ios) << 32) + static_cast<u32>(interrupt_type);
CoreTiming::ScheduleEvent((int)ticks_until_completion, s_finish_executing_command, userdata);
}
}
void FinishExecutingCommandCallback(u64 userdata, s64 cycles_late)
{
bool reply_to_ios = userdata >> 32 != 0;
DIInterruptType interrupt_type = static_cast<DIInterruptType>(userdata & 0xFFFFFFFF);
FinishExecutingCommand(reply_to_ios, interrupt_type);
}
void FinishExecutingCommand(bool reply_to_ios, DIInterruptType interrupt_type)
{
if (reply_to_ios)
{
std::shared_ptr<IWII_IPC_HLE_Device> di = WII_IPC_HLE_Interface::GetDeviceByName("/dev/di");
if (di)
std::static_pointer_cast<CWII_IPC_HLE_Device_di>(di)->FinishIOCtl(interrupt_type);
// If di == nullptr, IOS was probably shut down, so the command shouldn't be completed
}
else if (s_DICR.TSTART)
{
s_DICR.TSTART = 0;
s_DILENGTH.Length = 0;
GenerateDIInterrupt(interrupt_type);
}
}
// Simulates the timing aspects of reading data from a disc.

View File

@ -106,7 +106,8 @@ void ChangeDisc(const std::string& fileName);
// DVD Access Functions
bool ChangePartition(u64 offset);
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);
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);
} // end of namespace DVDInterface

View File

@ -30,7 +30,7 @@ namespace DVDThread
static void DVDThread();
static void FinishRead(u64 userdata, s64 cyclesLate);
static void FinishRead(u64 userdata, s64 cycles_late);
static int s_finish_read;
static std::thread s_dvd_thread;
@ -47,9 +47,9 @@ 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;
// 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.
static bool s_reply_to_ios;
// The following time variables are only used for logging
static u64 s_realtime_started_us;
@ -89,7 +89,7 @@ void DoState(PointerWrap &p)
p.Do(s_output_address);
p.Do(s_length);
p.Do(s_decrypt);
p.Do(s_callback_event_type);
p.Do(s_reply_to_ios);
// s_realtime_started_us and s_realtime_done_us aren't savestated
// because they rely on the current system's time.
@ -109,7 +109,7 @@ void WaitUntilIdle()
}
void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt,
int callback_event_type, int ticks_until_completion)
bool reply_to_ios, int ticks_until_completion)
{
_assert_(Core::IsCPUThread());
@ -119,7 +119,7 @@ void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt,
s_output_address = output_address;
s_length = length;
s_decrypt = decrypt;
s_callback_event_type = callback_event_type;
s_reply_to_ios = reply_to_ios;
s_time_read_started = CoreTiming::GetTicks();
s_realtime_started_us = Common::Timer::GetTimeUs();
@ -129,7 +129,7 @@ void StartRead(u64 dvd_offset, u32 output_address, u32 length, bool decrypt,
CoreTiming::ScheduleEvent(ticks_until_completion, s_finish_read);
}
static void FinishRead(u64 userdata, s64 cyclesLate)
static void FinishRead(u64 userdata, s64 cycles_late)
{
WaitUntilIdle();
@ -151,7 +151,7 @@ static void FinishRead(u64 userdata, s64 cyclesLate)
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);
DVDInterface::FinishExecutingCommand(s_reply_to_ios, DVDInterface::INT_TCINT);
}
static void DVDThread()

View File

@ -16,6 +16,6 @@ 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);
bool reply_to_ios, int ticks_until_completion);
}

View File

@ -16,21 +16,9 @@
#include "Core/IPC_HLE/WII_IPC_HLE.h"
#include "Core/IPC_HLE/WII_IPC_HLE_Device_DI.h"
static int ioctl_callback;
static void IOCtlCallback(u64 userdata, s64 cycles_late)
{
std::shared_ptr<IWII_IPC_HLE_Device> di = WII_IPC_HLE_Interface::GetDeviceByName("/dev/di");
if (di)
std::static_pointer_cast<CWII_IPC_HLE_Device_di>(di)->FinishIOCtl((DVDInterface::DIInterruptType)userdata);
// If di == 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)
{
ioctl_callback = CoreTiming::RegisterEvent("IOCtlCallbackDI", IOCtlCallback);
}
CWII_IPC_HLE_Device_di::~CWII_IPC_HLE_Device_di()
@ -101,8 +89,7 @@ void CWII_IPC_HLE_Device_di::StartIOCtl(u32 command_address)
// 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);
DVDInterface::ExecuteCommand(command_0, command_1, command_2, BufferOut, BufferOutSize, true);
}
void CWII_IPC_HLE_Device_di::FinishIOCtl(DVDInterface::DIInterruptType interrupt_type)

View File

@ -71,7 +71,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 = 52; // Last changed in PR 3667
static const u32 STATE_VERSION = 53; // Last changed in PR 3759
// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,