diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 9cb907a9d5..0c0b4ddf17 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -133,53 +133,60 @@ union UDICFG explicit UDICFG(u32 hex) : Hex{hex} {} }; -// STATE_TO_SAVE +struct DVDInterfaceState::Data +{ + // Hardware registers + UDISR DISR; + UDICVR DICVR; + u32 DICMDBUF[3]; + u32 DIMAR; + u32 DILENGTH; + UDICR DICR; + u32 DIIMMBUF; + UDICFG DICFG; -// Hardware registers -static UDISR s_DISR; -static UDICVR s_DICVR; -static u32 s_DICMDBUF[3]; -static u32 s_DIMAR; -static u32 s_DILENGTH; -static UDICR s_DICR; -static u32 s_DIIMMBUF; -static UDICFG s_DICFG; + StreamADPCM::ADPCMDecoder adpcm_decoder; -static StreamADPCM::ADPCMDecoder s_adpcm_decoder; + // DTK + bool stream = false; + bool stop_at_track_end = false; + u64 audio_position; + u64 current_start; + u32 current_length; + u64 next_start; + u32 next_length; + u32 pending_samples; + bool enable_dtk = false; + u8 dtk_buffer_length = 0; // TODO: figure out how this affects the regular buffer -// DTK -static bool s_stream = false; -static bool s_stop_at_track_end = false; -static u64 s_audio_position; -static u64 s_current_start; -static u32 s_current_length; -static u64 s_next_start; -static u32 s_next_length; -static u32 s_pending_samples; -static bool s_enable_dtk = false; -static u8 s_dtk_buffer_length = 0; // TODO: figure out how this affects the regular buffer + // Disc drive state + DriveState drive_state; + DriveError error_code; + u64 disc_end_offset; -// Disc drive state -static DriveState s_drive_state; -static DriveError s_error_code; -static u64 s_disc_end_offset; + // Disc drive timing + u64 read_buffer_start_time; + u64 read_buffer_end_time; + u64 read_buffer_start_offset; + u64 read_buffer_end_offset; -// Disc drive timing -static u64 s_read_buffer_start_time; -static u64 s_read_buffer_end_time; -static u64 s_read_buffer_start_offset; -static u64 s_read_buffer_end_offset; + // Disc changing + std::string disc_path_to_insert; + std::vector auto_disc_change_paths; + size_t auto_disc_change_index; -// Disc changing -static std::string s_disc_path_to_insert; -static std::vector s_auto_disc_change_paths; -static size_t s_auto_disc_change_index; + // Events + CoreTiming::EventType* finish_executing_command; + CoreTiming::EventType* auto_change_disc; + CoreTiming::EventType* eject_disc; + CoreTiming::EventType* insert_disc; +}; -// Events -static CoreTiming::EventType* s_finish_executing_command; -static CoreTiming::EventType* s_auto_change_disc; -static CoreTiming::EventType* s_eject_disc; -static CoreTiming::EventType* s_insert_disc; +DVDInterfaceState::DVDInterfaceState() : m_data(std::make_unique()) +{ +} + +DVDInterfaceState::~DVDInterfaceState() = default; static void AutoChangeDiscCallback(u64 userdata, s64 cyclesLate); static void EjectDiscCallback(u64 userdata, s64 cyclesLate); @@ -202,48 +209,53 @@ static void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& parti void DoState(PointerWrap& p) { - p.DoPOD(s_DISR); - p.DoPOD(s_DICVR); - p.DoArray(s_DICMDBUF); - p.Do(s_DIMAR); - p.Do(s_DILENGTH); - p.Do(s_DICR); - p.Do(s_DIIMMBUF); - p.DoPOD(s_DICFG); + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); - p.Do(s_stream); - p.Do(s_stop_at_track_end); - p.Do(s_audio_position); - p.Do(s_current_start); - p.Do(s_current_length); - p.Do(s_next_start); - p.Do(s_next_length); - p.Do(s_pending_samples); - p.Do(s_enable_dtk); - p.Do(s_dtk_buffer_length); + p.DoPOD(state.DISR); + p.DoPOD(state.DICVR); + p.DoArray(state.DICMDBUF); + p.Do(state.DIMAR); + p.Do(state.DILENGTH); + p.Do(state.DICR); + p.Do(state.DIIMMBUF); + p.DoPOD(state.DICFG); - p.Do(s_drive_state); - p.Do(s_error_code); + p.Do(state.stream); + p.Do(state.stop_at_track_end); + p.Do(state.audio_position); + p.Do(state.current_start); + p.Do(state.current_length); + p.Do(state.next_start); + p.Do(state.next_length); + p.Do(state.pending_samples); + p.Do(state.enable_dtk); + p.Do(state.dtk_buffer_length); - p.Do(s_read_buffer_start_time); - p.Do(s_read_buffer_end_time); - p.Do(s_read_buffer_start_offset); - p.Do(s_read_buffer_end_offset); + p.Do(state.drive_state); + p.Do(state.error_code); - p.Do(s_disc_path_to_insert); + p.Do(state.read_buffer_start_time); + p.Do(state.read_buffer_end_time); + p.Do(state.read_buffer_start_offset); + p.Do(state.read_buffer_end_offset); + + p.Do(state.disc_path_to_insert); DVDThread::DoState(p); - s_adpcm_decoder.DoState(p); + state.adpcm_decoder.DoState(p); } static size_t ProcessDTKSamples(std::vector* temp_pcm, const std::vector& audio_data) { + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + size_t samples_processed = 0; size_t bytes_processed = 0; while (samples_processed < temp_pcm->size() / 2 && bytes_processed < audio_data.size()) { - s_adpcm_decoder.DecodeBlock(&(*temp_pcm)[samples_processed * 2], &audio_data[bytes_processed]); + state.adpcm_decoder.DecodeBlock(&(*temp_pcm)[samples_processed * 2], + &audio_data[bytes_processed]); for (size_t i = 0; i < StreamADPCM::SAMPLES_PER_BLOCK * 2; ++i) { // TODO: Fix the mixer so it can accept non-byte-swapped samples. @@ -258,33 +270,35 @@ static size_t ProcessDTKSamples(std::vector* temp_pcm, const std::vector= s_current_start + s_current_length) + if (state.audio_position >= state.current_start + state.current_length) { DEBUG_LOG_FMT(DVDINTERFACE, "AdvanceDTK: NextStart={:08x}, NextLength={:08x}, " "CurrentStart={:08x}, CurrentLength={:08x}, AudioPos={:08x}", - s_next_start, s_next_length, s_current_start, s_current_length, - s_audio_position); + state.next_start, state.next_length, state.current_start, state.current_length, + state.audio_position); - s_audio_position = s_next_start; - s_current_start = s_next_start; - s_current_length = s_next_length; + state.audio_position = state.next_start; + state.current_start = state.next_start; + state.current_length = state.next_length; - if (s_stop_at_track_end) + if (state.stop_at_track_end) { - s_stop_at_track_end = false; - s_stream = false; + state.stop_at_track_end = false; + state.stream = false; break; } - s_adpcm_decoder.ResetFilter(); + state.adpcm_decoder.ResetFilter(); } - s_audio_position += StreamADPCM::ONE_BLOCK_SIZE; + state.audio_position += StreamADPCM::ONE_BLOCK_SIZE; bytes_to_process += StreamADPCM::ONE_BLOCK_SIZE; *samples_to_process += StreamADPCM::SAMPLES_PER_BLOCK; } @@ -295,6 +309,8 @@ static u32 AdvanceDTK(u32 maximum_samples, u32* samples_to_process) static void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vector& audio_data, s64 cycles_late) { + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + // Actual games always set this to 48 KHz // but let's make sure to use GetAISSampleRateDivisor() // just in case it changes to 32 KHz @@ -311,32 +327,32 @@ static void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vect if (interrupt_type == DIInterruptType::TCINT) { // Send audio to the mixer. - std::vector temp_pcm(s_pending_samples * 2, 0); + std::vector temp_pcm(state.pending_samples * 2, 0); ProcessDTKSamples(&temp_pcm, audio_data); auto& system = Core::System::GetInstance(); SoundStream* sound_stream = system.GetSoundStream(); - sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(), s_pending_samples); + sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(), state.pending_samples); - if (s_stream && AudioInterface::IsPlaying()) + if (state.stream && AudioInterface::IsPlaying()) { - read_offset = s_audio_position; - read_length = AdvanceDTK(maximum_samples, &s_pending_samples); + read_offset = state.audio_position; + read_length = AdvanceDTK(maximum_samples, &state.pending_samples); } else { read_length = 0; - s_pending_samples = maximum_samples; + state.pending_samples = maximum_samples; } } else { read_length = 0; - s_pending_samples = maximum_samples; + state.pending_samples = maximum_samples; } // Read the next chunk of audio data asynchronously. - s64 ticks_to_dtk = SystemTimers::GetTicksPerSecond() * s64(s_pending_samples) * + s64 ticks_to_dtk = SystemTimers::GetTicksPerSecond() * s64(state.pending_samples) * sample_rate_divisor / Mixer::FIXED_SAMPLE_RATE_DIVIDEND; ticks_to_dtk -= cycles_late; if (read_length > 0) @@ -348,7 +364,7 @@ static void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vect { // There's nothing to read, so using DVDThread is unnecessary. u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::TCINT); - CoreTiming::ScheduleEvent(ticks_to_dtk, s_finish_executing_command, userdata); + CoreTiming::ScheduleEvent(ticks_to_dtk, state.finish_executing_command, userdata); } } @@ -358,45 +374,47 @@ void Init() DVDThread::Start(); - s_DISR.Hex = 0; - s_DICVR.Hex = 1; // Disc Channel relies on cover being open when no disc is inserted - s_DICMDBUF[0] = 0; - s_DICMDBUF[1] = 0; - s_DICMDBUF[2] = 0; - s_DIMAR = 0; - s_DILENGTH = 0; - s_DICR.Hex = 0; - s_DIIMMBUF = 0; - s_DICFG.Hex = 0; - s_DICFG.CONFIG = 1; // Disable bootrom descrambler + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + state.DISR.Hex = 0; + state.DICVR.Hex = 1; // Disc Channel relies on cover being open when no disc is inserted + state.DICMDBUF[0] = 0; + state.DICMDBUF[1] = 0; + state.DICMDBUF[2] = 0; + state.DIMAR = 0; + state.DILENGTH = 0; + state.DICR.Hex = 0; + state.DIIMMBUF = 0; + state.DICFG.Hex = 0; + state.DICFG.CONFIG = 1; // Disable bootrom descrambler ResetDrive(false); - s_auto_change_disc = CoreTiming::RegisterEvent("AutoChangeDisc", AutoChangeDiscCallback); - s_eject_disc = CoreTiming::RegisterEvent("EjectDisc", EjectDiscCallback); - s_insert_disc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback); + state.auto_change_disc = CoreTiming::RegisterEvent("AutoChangeDisc", AutoChangeDiscCallback); + state.eject_disc = CoreTiming::RegisterEvent("EjectDisc", EjectDiscCallback); + state.insert_disc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback); - s_finish_executing_command = + state.finish_executing_command = CoreTiming::RegisterEvent("FinishExecutingCommand", FinishExecutingCommandCallback); u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::TCINT); - CoreTiming::ScheduleEvent(0, s_finish_executing_command, userdata); + CoreTiming::ScheduleEvent(0, state.finish_executing_command, userdata); } // Resets state on the MN102 chip in the drive itself, but not the DI registers exposed on the // emulated device, or any inserted disc. void ResetDrive(bool spinup) { - s_stream = false; - s_stop_at_track_end = false; - s_audio_position = 0; - s_next_start = 0; - s_next_length = 0; - s_current_start = 0; - s_current_length = 0; - s_pending_samples = 0; - s_enable_dtk = false; - s_dtk_buffer_length = 0; + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + state.stream = false; + state.stop_at_track_end = false; + state.audio_position = 0; + state.next_start = 0; + state.next_length = 0; + state.current_start = 0; + state.current_length = 0; + state.pending_samples = 0; + state.enable_dtk = false; + state.dtk_buffer_length = 0; if (!IsDiscInside()) { @@ -421,10 +439,10 @@ void ResetDrive(bool spinup) SetDriveError(DriveError::None); // The buffer is empty at start - s_read_buffer_start_offset = 0; - s_read_buffer_end_offset = 0; - s_read_buffer_start_time = 0; - s_read_buffer_end_time = 0; + state.read_buffer_start_offset = 0; + state.read_buffer_end_offset = 0; + state.read_buffer_start_time = 0; + state.read_buffer_end_time = 0; } void Shutdown() @@ -465,11 +483,12 @@ void SetDisc(std::unique_ptr disc, bool had_disc = IsDiscInside(); bool has_disc = static_cast(disc); + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); if (has_disc) { - s_disc_end_offset = GetDiscEndOffset(*disc); + state.disc_end_offset = GetDiscEndOffset(*disc); if (disc->GetDataSizeType() != DiscIO::DataSizeType::Accurate) - WARN_LOG_FMT(DVDINTERFACE, "Unknown disc size, guessing {0} bytes", s_disc_end_offset); + WARN_LOG_FMT(DVDINTERFACE, "Unknown disc size, guessing {0} bytes", state.disc_end_offset); const DiscIO::BlobReader& blob = disc->GetBlobReader(); @@ -493,8 +512,8 @@ void SetDisc(std::unique_ptr disc, ASSERT_MSG(DISCIO, (*auto_disc_change_paths).size() != 1, "Cannot automatically change between one disc"); - s_auto_disc_change_paths = *auto_disc_change_paths; - s_auto_disc_change_index = 0; + state.auto_disc_change_paths = *auto_disc_change_paths; + state.auto_disc_change_index = 0; } // Assume that inserting a disc requires having an empty disc before @@ -524,20 +543,22 @@ static void EjectDiscCallback(u64 userdata, s64 cyclesLate) static void InsertDiscCallback(u64 userdata, s64 cyclesLate) { - std::unique_ptr new_disc = DiscIO::CreateDisc(s_disc_path_to_insert); + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + std::unique_ptr new_disc = DiscIO::CreateDisc(state.disc_path_to_insert); if (new_disc) SetDisc(std::move(new_disc), {}); else PanicAlertFmtT("The disc that was about to be inserted couldn't be found."); - s_disc_path_to_insert.clear(); + state.disc_path_to_insert.clear(); } // Must only be called on the CPU thread void EjectDisc(EjectCause cause) { - CoreTiming::ScheduleEvent(0, s_eject_disc); + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + CoreTiming::ScheduleEvent(0, state.eject_disc); if (cause == EjectCause::User) ExpansionInterface::g_rtc_flags[ExpansionInterface::RTCFlag::EjectButton] = true; } @@ -549,8 +570,9 @@ void ChangeDisc(const std::vector& paths) if (paths.size() > 1) { - s_auto_disc_change_paths = paths; - s_auto_disc_change_index = 0; + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + state.auto_disc_change_paths = paths; + state.auto_disc_change_index = 0; } ChangeDisc(paths[0]); @@ -559,7 +581,8 @@ void ChangeDisc(const std::vector& paths) // Must only be called on the CPU thread void ChangeDisc(const std::string& new_path) { - if (!s_disc_path_to_insert.empty()) + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + if (!state.disc_path_to_insert.empty()) { PanicAlertFmtT("A disc is already about to be inserted."); return; @@ -567,38 +590,41 @@ void ChangeDisc(const std::string& new_path) EjectDisc(EjectCause::User); - s_disc_path_to_insert = new_path; - CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond(), s_insert_disc); + state.disc_path_to_insert = new_path; + CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond(), state.insert_disc); Movie::SignalDiscChange(new_path); - for (size_t i = 0; i < s_auto_disc_change_paths.size(); ++i) + for (size_t i = 0; i < state.auto_disc_change_paths.size(); ++i) { - if (s_auto_disc_change_paths[i] == new_path) + if (state.auto_disc_change_paths[i] == new_path) { - s_auto_disc_change_index = i; + state.auto_disc_change_index = i; return; } } - s_auto_disc_change_paths.clear(); + state.auto_disc_change_paths.clear(); } // Must only be called on the CPU thread bool AutoChangeDisc() { - if (s_auto_disc_change_paths.empty()) + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + if (state.auto_disc_change_paths.empty()) return false; - s_auto_disc_change_index = (s_auto_disc_change_index + 1) % s_auto_disc_change_paths.size(); - ChangeDisc(s_auto_disc_change_paths[s_auto_disc_change_index]); + state.auto_disc_change_index = + (state.auto_disc_change_index + 1) % state.auto_disc_change_paths.size(); + ChangeDisc(state.auto_disc_change_paths[state.auto_disc_change_index]); return true; } static void SetLidOpen() { - const u32 old_value = s_DICVR.CVR; - s_DICVR.CVR = IsDiscInside() ? 0 : 1; - if (s_DICVR.CVR != old_value) + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + const u32 old_value = state.DICVR.CVR; + state.DICVR.CVR = IsDiscInside() ? 0 : 1; + if (state.DICVR.CVR != old_value) GenerateDIInterrupt(DIInterruptType::CVRINT); } @@ -612,25 +638,27 @@ bool UpdateRunningGameMetadata(std::optional title_id) void RegisterMMIO(MMIO::Mapping* mmio, u32 base, bool is_wii) { - mmio->Register(base | DI_STATUS_REGISTER, MMIO::DirectRead(&s_DISR.Hex), + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + mmio->Register(base | DI_STATUS_REGISTER, MMIO::DirectRead(&state.DISR.Hex), MMIO::ComplexWrite([](u32, u32 val) { + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); const UDISR tmp_status_reg(val); - s_DISR.DEINTMASK = tmp_status_reg.DEINTMASK.Value(); - s_DISR.TCINTMASK = tmp_status_reg.TCINTMASK.Value(); - s_DISR.BRKINTMASK = tmp_status_reg.BRKINTMASK.Value(); - s_DISR.BREAK = tmp_status_reg.BREAK.Value(); + state.DISR.DEINTMASK = tmp_status_reg.DEINTMASK.Value(); + state.DISR.TCINTMASK = tmp_status_reg.TCINTMASK.Value(); + state.DISR.BRKINTMASK = tmp_status_reg.BRKINTMASK.Value(); + state.DISR.BREAK = tmp_status_reg.BREAK.Value(); if (tmp_status_reg.DEINT) - s_DISR.DEINT = 0; + state.DISR.DEINT = 0; if (tmp_status_reg.TCINT) - s_DISR.TCINT = 0; + state.DISR.TCINT = 0; if (tmp_status_reg.BRKINT) - s_DISR.BRKINT = 0; + state.DISR.BRKINT = 0; - if (s_DISR.BREAK) + if (state.DISR.BREAK) { DEBUG_ASSERT(0); } @@ -638,25 +666,26 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base, bool is_wii) UpdateInterrupts(); })); - mmio->Register(base | DI_COVER_REGISTER, MMIO::DirectRead(&s_DICVR.Hex), + mmio->Register(base | DI_COVER_REGISTER, MMIO::DirectRead(&state.DICVR.Hex), MMIO::ComplexWrite([](u32, u32 val) { + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); const UDICVR tmp_cover_reg(val); - s_DICVR.CVRINTMASK = tmp_cover_reg.CVRINTMASK.Value(); + state.DICVR.CVRINTMASK = tmp_cover_reg.CVRINTMASK.Value(); if (tmp_cover_reg.CVRINT) - s_DICVR.CVRINT = 0; + state.DICVR.CVRINT = 0; UpdateInterrupts(); })); // Command registers, which have no special logic - mmio->Register(base | DI_COMMAND_0, MMIO::DirectRead(&s_DICMDBUF[0]), - MMIO::DirectWrite(&s_DICMDBUF[0])); - mmio->Register(base | DI_COMMAND_1, MMIO::DirectRead(&s_DICMDBUF[1]), - MMIO::DirectWrite(&s_DICMDBUF[1])); - mmio->Register(base | DI_COMMAND_2, MMIO::DirectRead(&s_DICMDBUF[2]), - MMIO::DirectWrite(&s_DICMDBUF[2])); + mmio->Register(base | DI_COMMAND_0, MMIO::DirectRead(&state.DICMDBUF[0]), + MMIO::DirectWrite(&state.DICMDBUF[0])); + mmio->Register(base | DI_COMMAND_1, MMIO::DirectRead(&state.DICMDBUF[1]), + MMIO::DirectWrite(&state.DICMDBUF[1])); + mmio->Register(base | DI_COMMAND_2, MMIO::DirectRead(&state.DICMDBUF[2]), + MMIO::DirectWrite(&state.DICMDBUF[2])); // DMA related registers. Mostly direct accesses (+ masking for writes to // handle things like address alignment) and complex write on the DMA @@ -671,32 +700,35 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base, bool is_wii) // address (0x0D006000), although we allow writes to both of these addresses if Dolphin was // started in Wii mode. (Also, normally in Wii mode the DI MMIOs are only written by the // IOS /dev/di module, but we *do* emulate /dev/di writing the DI MMIOs.) - mmio->Register(base | DI_DMA_ADDRESS_REGISTER, MMIO::DirectRead(&s_DIMAR), - MMIO::DirectWrite(&s_DIMAR, is_wii ? ~0x1F : ~0xFC00001F)); - mmio->Register(base | DI_DMA_LENGTH_REGISTER, MMIO::DirectRead(&s_DILENGTH), - MMIO::DirectWrite(&s_DILENGTH, ~0x1F)); - mmio->Register(base | DI_DMA_CONTROL_REGISTER, MMIO::DirectRead(&s_DICR.Hex), + mmio->Register(base | DI_DMA_ADDRESS_REGISTER, MMIO::DirectRead(&state.DIMAR), + MMIO::DirectWrite(&state.DIMAR, is_wii ? ~0x1F : ~0xFC00001F)); + mmio->Register(base | DI_DMA_LENGTH_REGISTER, MMIO::DirectRead(&state.DILENGTH), + MMIO::DirectWrite(&state.DILENGTH, ~0x1F)); + mmio->Register(base | DI_DMA_CONTROL_REGISTER, MMIO::DirectRead(&state.DICR.Hex), MMIO::ComplexWrite([](u32, u32 val) { - s_DICR.Hex = val & 7; - if (s_DICR.TSTART) + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + state.DICR.Hex = val & 7; + if (state.DICR.TSTART) { ExecuteCommand(ReplyType::Interrupt); } })); - mmio->Register(base | DI_IMMEDIATE_DATA_BUFFER, MMIO::DirectRead(&s_DIIMMBUF), - MMIO::DirectWrite(&s_DIIMMBUF)); + mmio->Register(base | DI_IMMEDIATE_DATA_BUFFER, MMIO::DirectRead(&state.DIIMMBUF), + MMIO::DirectWrite(&state.DIIMMBUF)); // DI config register is read only. - mmio->Register(base | DI_CONFIG_REGISTER, MMIO::DirectRead(&s_DICFG.Hex), + mmio->Register(base | DI_CONFIG_REGISTER, MMIO::DirectRead(&state.DICFG.Hex), MMIO::InvalidWrite()); } static void UpdateInterrupts() { - const bool set_mask = - (s_DISR.DEINT & s_DISR.DEINTMASK) != 0 || (s_DISR.TCINT & s_DISR.TCINTMASK) != 0 || - (s_DISR.BRKINT & s_DISR.BRKINTMASK) != 0 || (s_DICVR.CVRINT & s_DICVR.CVRINTMASK) != 0; + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + const bool set_mask = (state.DISR.DEINT & state.DISR.DEINTMASK) != 0 || + (state.DISR.TCINT & state.DISR.TCINTMASK) != 0 || + (state.DISR.BRKINT & state.DISR.BRKINTMASK) != 0 || + (state.DICVR.CVRINT & state.DICVR.CVRINTMASK) != 0; ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_DI, set_mask); @@ -706,19 +738,20 @@ static void UpdateInterrupts() static void GenerateDIInterrupt(DIInterruptType dvd_interrupt) { + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); switch (dvd_interrupt) { case DIInterruptType::DEINT: - s_DISR.DEINT = true; + state.DISR.DEINT = true; break; case DIInterruptType::TCINT: - s_DISR.TCINT = true; + state.DISR.TCINT = true; break; case DIInterruptType::BRKINT: - s_DISR.BRKINT = true; + state.DISR.BRKINT = true; break; case DIInterruptType::CVRINT: - s_DICVR.CVRINT = true; + state.DICVR.CVRINT = true; break; } @@ -727,38 +760,40 @@ static void GenerateDIInterrupt(DIInterruptType dvd_interrupt) void SetInterruptEnabled(DIInterruptType interrupt, bool enabled) { + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); switch (interrupt) { case DIInterruptType::DEINT: - s_DISR.DEINTMASK = enabled; + state.DISR.DEINTMASK = enabled; break; case DIInterruptType::TCINT: - s_DISR.TCINTMASK = enabled; + state.DISR.TCINTMASK = enabled; break; case DIInterruptType::BRKINT: - s_DISR.BRKINTMASK = enabled; + state.DISR.BRKINTMASK = enabled; break; case DIInterruptType::CVRINT: - s_DICVR.CVRINTMASK = enabled; + state.DICVR.CVRINTMASK = enabled; break; } } void ClearInterrupt(DIInterruptType interrupt) { + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); switch (interrupt) { case DIInterruptType::DEINT: - s_DISR.DEINT = false; + state.DISR.DEINT = false; break; case DIInterruptType::TCINT: - s_DISR.TCINT = false; + state.DISR.TCINT = false; break; case DIInterruptType::BRKINT: - s_DISR.BRKINT = false; + state.DISR.BRKINT = false; break; case DIInterruptType::CVRINT: - s_DICVR.CVRINT = false; + state.DICVR.CVRINT = false; break; } } @@ -768,25 +803,26 @@ void ClearInterrupt(DIInterruptType interrupt) // should issue a DEINT interrupt. static bool CheckReadPreconditions() { + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); if (!IsDiscInside()) // Implies CoverOpened or NoMediumPresent { ERROR_LOG_FMT(DVDINTERFACE, "No disc inside."); SetDriveError(DriveError::MediumNotPresent); return false; } - if (s_drive_state == DriveState::DiscChangeDetected) + if (state.drive_state == DriveState::DiscChangeDetected) { ERROR_LOG_FMT(DVDINTERFACE, "Disc changed (motor stopped)."); SetDriveError(DriveError::MediumChanged); return false; } - if (s_drive_state == DriveState::MotorStopped) + if (state.drive_state == DriveState::MotorStopped) { ERROR_LOG_FMT(DVDINTERFACE, "Motor stopped."); SetDriveError(DriveError::MotorStopped); return false; } - if (s_drive_state == DriveState::DiscIdNotRead) + if (state.drive_state == DriveState::DiscIdNotRead) { ERROR_LOG_FMT(DVDINTERFACE, "Disc id not read."); SetDriveError(DriveError::NoDiscID); @@ -800,6 +836,7 @@ static bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_lengt u32 output_length, const DiscIO::Partition& partition, ReplyType reply_type, DIInterruptType* interrupt_type) { + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); if (!CheckReadPreconditions()) { // Disc read fails @@ -823,7 +860,7 @@ static bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_lengt // regular DVD but just before the end of a DVD-R, displaying "Error #001" and failing to boot // if the read succeeds, so it's critical that we set the correct error code for such reads. // See https://wiibrew.org/wiki//dev/di#0x8D_DVDLowUnencryptedRead for details on Error #001. - if (dvd_offset + dvd_length > s_disc_end_offset) + if (dvd_offset + dvd_length > state.disc_end_offset) { SetDriveError(DriveError::BlockOOB); *interrupt_type = DIInterruptType::DEINT; @@ -839,23 +876,25 @@ static bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_lengt // with the userdata set to the interrupt type. void ExecuteCommand(ReplyType reply_type) { + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); DIInterruptType interrupt_type = DIInterruptType::TCINT; bool command_handled_by_thread = false; // DVDLowRequestError needs access to the error code set by the previous command - if (static_cast(s_DICMDBUF[0] >> 24) != DICommand::RequestError) + if (static_cast(state.DICMDBUF[0] >> 24) != DICommand::RequestError) SetDriveError(DriveError::None); - switch (static_cast(s_DICMDBUF[0] >> 24)) + switch (static_cast(state.DICMDBUF[0] >> 24)) { // Used by both GC and Wii case DICommand::Inquiry: // (shuffle2) Taken from my Wii - Memory::Write_U32(0x00000002, s_DIMAR); // Revision level, device code - Memory::Write_U32(0x20060526, s_DIMAR + 4); // Release date - Memory::Write_U32(0x41000000, s_DIMAR + 8); // Version + Memory::Write_U32(0x00000002, state.DIMAR); // Revision level, device code + Memory::Write_U32(0x20060526, state.DIMAR + 4); // Release date + Memory::Write_U32(0x41000000, state.DIMAR + 8); // Version - INFO_LOG_FMT(DVDINTERFACE, "DVDLowInquiry (Buffer {:#010x}, {:#x})", s_DIMAR, s_DILENGTH); + INFO_LOG_FMT(DVDINTERFACE, "DVDLowInquiry (Buffer {:#010x}, {:#x})", state.DIMAR, + state.DILENGTH); break; // GC-only patched drive firmware command, used by libogc @@ -877,33 +916,33 @@ void ExecuteCommand(ReplyType reply_type) // DMA Read from Disc. Only used through direct access on GC; direct use is prohibited by // IOS (which uses it internally) case DICommand::Read: - switch (s_DICMDBUF[0] & 0xFF) + switch (state.DICMDBUF[0] & 0xFF) { case 0x00: // Read Sector { - const u64 dvd_offset = static_cast(s_DICMDBUF[1]) << 2; + const u64 dvd_offset = static_cast(state.DICMDBUF[1]) << 2; INFO_LOG_FMT( DVDINTERFACE, "Read: DVDOffset={:08x}, DMABuffer = {:08x}, SrcLength = {:08x}, DMALength = {:08x}", - dvd_offset, s_DIMAR, s_DICMDBUF[2], s_DILENGTH); + dvd_offset, state.DIMAR, state.DICMDBUF[2], state.DILENGTH); - if (s_drive_state == DriveState::ReadyNoReadsMade) + if (state.drive_state == DriveState::ReadyNoReadsMade) SetDriveState(DriveState::Ready); command_handled_by_thread = - ExecuteReadCommand(dvd_offset, s_DIMAR, s_DICMDBUF[2], s_DILENGTH, DiscIO::PARTITION_NONE, - reply_type, &interrupt_type); + ExecuteReadCommand(dvd_offset, state.DIMAR, state.DICMDBUF[2], state.DILENGTH, + DiscIO::PARTITION_NONE, reply_type, &interrupt_type); } break; case 0x40: // Read DiscID - INFO_LOG_FMT(DVDINTERFACE, "Read DiscID: buffer {:08x}", s_DIMAR); - if (s_drive_state == DriveState::DiscIdNotRead) + INFO_LOG_FMT(DVDINTERFACE, "Read DiscID: buffer {:08x}", state.DIMAR); + if (state.drive_state == DriveState::DiscIdNotRead) { SetDriveState(DriveState::ReadyNoReadsMade); } - else if (s_drive_state == DriveState::ReadyNoReadsMade) + else if (state.drive_state == DriveState::ReadyNoReadsMade) { // The first disc ID reading is required before DTK can be configured. // If the disc ID is read again (or any other read occurs), it no longer can @@ -911,12 +950,13 @@ void ExecuteCommand(ReplyType reply_type) SetDriveState(DriveState::Ready); } - command_handled_by_thread = ExecuteReadCommand( - 0, s_DIMAR, 0x20, s_DILENGTH, DiscIO::PARTITION_NONE, reply_type, &interrupt_type); + command_handled_by_thread = + ExecuteReadCommand(0, state.DIMAR, 0x20, state.DILENGTH, DiscIO::PARTITION_NONE, + reply_type, &interrupt_type); break; default: - ERROR_LOG_FMT(DVDINTERFACE, "Unknown read subcommand: {:08x}", s_DICMDBUF[0]); + ERROR_LOG_FMT(DVDINTERFACE, "Unknown read subcommand: {:08x}", state.DICMDBUF[0]); break; } break; @@ -925,12 +965,12 @@ void ExecuteCommand(ReplyType reply_type) case DICommand::Seek: // Currently unimplemented INFO_LOG_FMT(DVDINTERFACE, "Seek: offset={:09x} (ignoring)", - static_cast(s_DICMDBUF[1]) << 2); + static_cast(state.DICMDBUF[1]) << 2); break; // Wii-exclusive case DICommand::ReadDVDMetadata: - switch ((s_DICMDBUF[0] >> 16) & 0xFF) + switch ((state.DICMDBUF[0] >> 16) & 0xFF) { case 0: ERROR_LOG_FMT(DVDINTERFACE, "DVDLowReadDvdPhysical"); @@ -942,7 +982,7 @@ void ExecuteCommand(ReplyType reply_type) ERROR_LOG_FMT(DVDINTERFACE, "DVDLowReadDvdDiscKey"); break; default: - ERROR_LOG_FMT(DVDINTERFACE, "Unknown 0xAD subcommand in {:08x}", s_DICMDBUF[0]); + ERROR_LOG_FMT(DVDINTERFACE, "Unknown 0xAD subcommand in {:08x}", state.DICMDBUF[0]); break; } SetDriveError(DriveError::InvalidCommand); @@ -984,8 +1024,8 @@ void ExecuteCommand(ReplyType reply_type) // TODO: Read the .bca file that cleanrip generates, if it exists // Memory::CopyToEmu(output_address, bca_data, 0x40); - Memory::Memset(s_DIMAR, 0, 0x40); - Memory::Write_U8(1, s_DIMAR + 0x33); + Memory::Memset(state.DIMAR, 0, 0x40); + Memory::Write_U8(1, state.DIMAR + 0x33); break; // Wii-exclusive case DICommand::RequestDiscStatus: @@ -1019,14 +1059,14 @@ void ExecuteCommand(ReplyType reply_type) case DICommand::RequestError: { u32 drive_state; - if (s_drive_state == DriveState::Ready) + if (state.drive_state == DriveState::Ready) drive_state = 0; else - drive_state = static_cast(s_drive_state) - 1; + drive_state = static_cast(state.drive_state) - 1; - const u32 result = (drive_state << 24) | static_cast(s_error_code); + const u32 result = (drive_state << 24) | static_cast(state.error_code); INFO_LOG_FMT(DVDINTERFACE, "Requesting error... ({:#010x})", result); - s_DIIMMBUF = result; + state.DIIMMBUF = result; SetDriveError(DriveError::None); break; } @@ -1039,60 +1079,60 @@ void ExecuteCommand(ReplyType reply_type) { if (!CheckReadPreconditions()) { - ERROR_LOG_FMT(DVDINTERFACE, "Cannot play audio (command {:08x})", s_DICMDBUF[0]); + ERROR_LOG_FMT(DVDINTERFACE, "Cannot play audio (command {:08x})", state.DICMDBUF[0]); interrupt_type = DIInterruptType::DEINT; break; } - if (!s_enable_dtk) + if (!state.enable_dtk) { ERROR_LOG_FMT( DVDINTERFACE, "Attempted to change playing audio while audio is disabled! ({:08x} {:08x} {:08x})", - s_DICMDBUF[0], s_DICMDBUF[1], s_DICMDBUF[2]); + state.DICMDBUF[0], state.DICMDBUF[1], state.DICMDBUF[2]); SetDriveError(DriveError::NoAudioBuf); interrupt_type = DIInterruptType::DEINT; break; } - if (s_drive_state == DriveState::ReadyNoReadsMade) + if (state.drive_state == DriveState::ReadyNoReadsMade) SetDriveState(DriveState::Ready); - switch ((s_DICMDBUF[0] >> 16) & 0xFF) + switch ((state.DICMDBUF[0] >> 16) & 0xFF) { case 0x00: { - const u64 offset = static_cast(s_DICMDBUF[1]) << 2; - const u32 length = s_DICMDBUF[2]; + const u64 offset = static_cast(state.DICMDBUF[1]) << 2; + const u32 length = state.DICMDBUF[2]; INFO_LOG_FMT(DVDINTERFACE, "(Audio) Start stream: offset: {:08x} length: {:08x}", offset, length); if ((offset == 0) && (length == 0)) { - s_stop_at_track_end = true; + state.stop_at_track_end = true; } - else if (!s_stop_at_track_end) + else if (!state.stop_at_track_end) { - s_next_start = offset; - s_next_length = length; - if (!s_stream) + state.next_start = offset; + state.next_length = length; + if (!state.stream) { - s_current_start = s_next_start; - s_current_length = s_next_length; - s_audio_position = s_current_start; - s_adpcm_decoder.ResetFilter(); - s_stream = true; + state.current_start = state.next_start; + state.current_length = state.next_length; + state.audio_position = state.current_start; + state.adpcm_decoder.ResetFilter(); + state.stream = true; } } break; } case 0x01: INFO_LOG_FMT(DVDINTERFACE, "(Audio) Stop stream"); - s_stop_at_track_end = false; - s_stream = false; + state.stop_at_track_end = false; + state.stream = false; break; default: - ERROR_LOG_FMT(DVDINTERFACE, "Invalid audio command! ({:08x} {:08x} {:08x})", s_DICMDBUF[0], - s_DICMDBUF[1], s_DICMDBUF[2]); + ERROR_LOG_FMT(DVDINTERFACE, "Invalid audio command! ({:08x} {:08x} {:08x})", + state.DICMDBUF[0], state.DICMDBUF[1], state.DICMDBUF[2]); SetDriveError(DriveError::InvalidAudioCommand); interrupt_type = DIInterruptType::DEINT; break; @@ -1110,7 +1150,7 @@ void ExecuteCommand(ReplyType reply_type) break; } - if (!s_enable_dtk) + if (!state.enable_dtk) { ERROR_LOG_FMT(DVDINTERFACE, "Attempted to request audio status while audio is disabled!"); SetDriveError(DriveError::NoAudioBuf); @@ -1118,36 +1158,36 @@ void ExecuteCommand(ReplyType reply_type) break; } - switch (s_DICMDBUF[0] >> 16 & 0xFF) + switch (state.DICMDBUF[0] >> 16 & 0xFF) { case 0x00: // Returns streaming status INFO_LOG_FMT(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); - s_DIIMMBUF = (s_stream ? 1 : 0); + state.audio_position, state.current_start + state.current_length, + state.current_start, state.current_length); + state.DIIMMBUF = (state.stream ? 1 : 0); break; case 0x01: // Returns the current offset INFO_LOG_FMT(DVDINTERFACE, "(Audio): Stream Status: Request Audio status AudioPos:{:08x}", - s_audio_position); - s_DIIMMBUF = static_cast((s_audio_position & 0xffffffffffff8000ull) >> 2); + state.audio_position); + state.DIIMMBUF = static_cast((state.audio_position & 0xffffffffffff8000ull) >> 2); break; case 0x02: // Returns the start offset INFO_LOG_FMT(DVDINTERFACE, "(Audio): Stream Status: Request Audio status CurrentStart:{:08x}", - s_current_start); - s_DIIMMBUF = static_cast(s_current_start >> 2); + state.current_start); + state.DIIMMBUF = static_cast(state.current_start >> 2); break; case 0x03: // Returns the total length INFO_LOG_FMT(DVDINTERFACE, "(Audio): Stream Status: Request Audio status CurrentLength:{:08x}", - s_current_length); - s_DIIMMBUF = s_current_length; + state.current_length); + state.DIIMMBUF = state.current_length; break; default: ERROR_LOG_FMT(DVDINTERFACE, "Invalid audio status command! ({:08x} {:08x} {:08x})", - s_DICMDBUF[0], s_DICMDBUF[1], s_DICMDBUF[2]); + state.DICMDBUF[0], state.DICMDBUF[1], state.DICMDBUF[2]); SetDriveError(DriveError::InvalidAudioCommand); interrupt_type = DIInterruptType::DEINT; break; @@ -1158,12 +1198,13 @@ void ExecuteCommand(ReplyType reply_type) // Used by both GC and Wii case DICommand::StopMotor: { - const bool eject = (s_DICMDBUF[0] & (1 << 17)); - const bool kill = (s_DICMDBUF[0] & (1 << 20)); + const bool eject = (state.DICMDBUF[0] & (1 << 17)); + const bool kill = (state.DICMDBUF[0] & (1 << 20)); INFO_LOG_FMT(DVDINTERFACE, "DVDLowStopMotor{}{}", eject ? " eject" : "", kill ? " kill!" : ""); - if (s_drive_state == DriveState::Ready || s_drive_state == DriveState::ReadyNoReadsMade || - s_drive_state == DriveState::DiscIdNotRead) + if (state.drive_state == DriveState::Ready || + state.drive_state == DriveState::ReadyNoReadsMade || + state.drive_state == DriveState::DiscIdNotRead) { SetDriveState(DriveState::MotorStopped); } @@ -1171,10 +1212,10 @@ void ExecuteCommand(ReplyType reply_type) const bool force_eject = eject && !kill; if (Config::Get(Config::MAIN_AUTO_DISC_CHANGE) && !Movie::IsPlayingInput() && - DVDThread::IsInsertedDiscRunning() && !s_auto_disc_change_paths.empty()) + DVDThread::IsInsertedDiscRunning() && !state.auto_disc_change_paths.empty()) { CoreTiming::ScheduleEvent(force_eject ? 0 : SystemTimers::GetTicksPerSecond() / 2, - s_auto_change_disc); + state.auto_change_disc); OSD::AddMessage("Changing discs automatically...", OSD::Duration::NORMAL); } else if (force_eject) @@ -1200,7 +1241,7 @@ void ExecuteCommand(ReplyType reply_type) break; } - if (s_drive_state == DriveState::Ready) + if (state.drive_state == DriveState::Ready) { ERROR_LOG_FMT(DVDINTERFACE, "Attempted to change DTK configuration after a read has been made!"); @@ -1211,7 +1252,7 @@ void ExecuteCommand(ReplyType reply_type) // Note that this can be called multiple times, as long as the drive is in the ReadyNoReadsMade // state. Calling it does not exit that state. - AudioBufferConfig((s_DICMDBUF[0] >> 16) & 1, s_DICMDBUF[0] & 0xf); + AudioBufferConfig((state.DICMDBUF[0] >> 16) & 1, state.DICMDBUF[0] & 0xf); break; // GC-only patched drive firmware command, used by libogc @@ -1226,7 +1267,7 @@ void ExecuteCommand(ReplyType reply_type) // This will appear as unknown commands, unless the check is re-instated to catch such data. // Can only be used through direct access and only after unlocked. case DICommand::Debug: - ERROR_LOG_FMT(DVDINTERFACE, "Unsupported DVD Drive debug command {:#010x}", s_DICMDBUF[0]); + ERROR_LOG_FMT(DVDINTERFACE, "Unsupported DVD Drive debug command {:#010x}", state.DICMDBUF[0]); SetDriveError(DriveError::InvalidCommand); interrupt_type = DIInterruptType::DEINT; break; @@ -1236,12 +1277,13 @@ void ExecuteCommand(ReplyType reply_type) // Can only be used through direct access. The unlock command doesn't seem to work on the Wii. case DICommand::DebugUnlock: { - if (s_DICMDBUF[0] == 0xFF014D41 && s_DICMDBUF[1] == 0x54534849 && s_DICMDBUF[2] == 0x54410200) + if (state.DICMDBUF[0] == 0xFF014D41 && state.DICMDBUF[1] == 0x54534849 && + state.DICMDBUF[2] == 0x54410200) { INFO_LOG_FMT(DVDINTERFACE, "Unlock test 1 passed"); } - else if (s_DICMDBUF[0] == 0xFF004456 && s_DICMDBUF[1] == 0x442D4741 && - s_DICMDBUF[2] == 0x4D450300) + else if (state.DICMDBUF[0] == 0xFF004456 && state.DICMDBUF[1] == 0x442D4741 && + state.DICMDBUF[2] == 0x4D450300) { INFO_LOG_FMT(DVDINTERFACE, "Unlock test 2 passed"); } @@ -1253,9 +1295,9 @@ void ExecuteCommand(ReplyType reply_type) break; default: - ERROR_LOG_FMT(DVDINTERFACE, "Unknown command {:#010x} (Buffer {:#010x}, {:#x})", s_DICMDBUF[0], - s_DIMAR, s_DILENGTH); - PanicAlertFmtT("Unknown DVD command {0:08x} - fatal error", s_DICMDBUF[0]); + ERROR_LOG_FMT(DVDINTERFACE, "Unknown command {:#010x} (Buffer {:#010x}, {:#x})", + state.DICMDBUF[0], state.DIMAR, state.DILENGTH); + PanicAlertFmtT("Unknown DVD command {0:08x} - fatal error", state.DICMDBUF[0]); SetDriveError(DriveError::InvalidCommand); interrupt_type = DIInterruptType::DEINT; break; @@ -1264,18 +1306,20 @@ void ExecuteCommand(ReplyType reply_type) if (!command_handled_by_thread) { // TODO: Needs testing to determine if MINIMUM_COMMAND_LATENCY_US is accurate for this - CoreTiming::ScheduleEvent( - MINIMUM_COMMAND_LATENCY_US * (SystemTimers::GetTicksPerSecond() / 1000000), - s_finish_executing_command, PackFinishExecutingCommandUserdata(reply_type, interrupt_type)); + CoreTiming::ScheduleEvent(MINIMUM_COMMAND_LATENCY_US * + (SystemTimers::GetTicksPerSecond() / 1000000), + state.finish_executing_command, + PackFinishExecutingCommandUserdata(reply_type, interrupt_type)); } } void PerformDecryptingRead(u32 position, u32 length, u32 output_address, const DiscIO::Partition& partition, ReplyType reply_type) { + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); DIInterruptType interrupt_type = DIInterruptType::TCINT; - if (s_drive_state == DriveState::ReadyNoReadsMade) + if (state.drive_state == DriveState::ReadyNoReadsMade) SetDriveState(DriveState::Ready); const bool command_handled_by_thread = @@ -1285,34 +1329,38 @@ void PerformDecryptingRead(u32 position, u32 length, u32 output_address, if (!command_handled_by_thread) { // TODO: Needs testing to determine if MINIMUM_COMMAND_LATENCY_US is accurate for this - CoreTiming::ScheduleEvent( - MINIMUM_COMMAND_LATENCY_US * (SystemTimers::GetTicksPerSecond() / 1000000), - s_finish_executing_command, PackFinishExecutingCommandUserdata(reply_type, interrupt_type)); + CoreTiming::ScheduleEvent(MINIMUM_COMMAND_LATENCY_US * + (SystemTimers::GetTicksPerSecond() / 1000000), + state.finish_executing_command, + PackFinishExecutingCommandUserdata(reply_type, interrupt_type)); } } void ForceOutOfBoundsRead(ReplyType reply_type) { + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); INFO_LOG_FMT(DVDINTERFACE, "Forcing an out-of-bounds disc read."); - if (s_drive_state == DriveState::ReadyNoReadsMade) + if (state.drive_state == DriveState::ReadyNoReadsMade) SetDriveState(DriveState::Ready); SetDriveError(DriveError::BlockOOB); // TODO: Needs testing to determine if MINIMUM_COMMAND_LATENCY_US is accurate for this const DIInterruptType interrupt_type = DIInterruptType::DEINT; - CoreTiming::ScheduleEvent( - MINIMUM_COMMAND_LATENCY_US * (SystemTimers::GetTicksPerSecond() / 1000000), - s_finish_executing_command, PackFinishExecutingCommandUserdata(reply_type, interrupt_type)); + CoreTiming::ScheduleEvent(MINIMUM_COMMAND_LATENCY_US * + (SystemTimers::GetTicksPerSecond() / 1000000), + state.finish_executing_command, + PackFinishExecutingCommandUserdata(reply_type, interrupt_type)); } void AudioBufferConfig(bool enable_dtk, u8 dtk_buffer_length) { - s_enable_dtk = enable_dtk; - s_dtk_buffer_length = dtk_buffer_length; - if (s_enable_dtk) - INFO_LOG_FMT(DVDINTERFACE, "DTK enabled: buffer size {}", s_dtk_buffer_length); + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + state.enable_dtk = enable_dtk; + state.dtk_buffer_length = dtk_buffer_length; + if (state.enable_dtk) + INFO_LOG_FMT(DVDINTERFACE, "DTK enabled: buffer size {}", state.dtk_buffer_length); else INFO_LOG_FMT(DVDINTERFACE, "DTK disabled"); } @@ -1331,17 +1379,21 @@ void FinishExecutingCommandCallback(u64 userdata, s64 cycles_late) void SetDriveState(DriveState state) { - s_drive_state = state; + auto& dvd_interface_state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + dvd_interface_state.drive_state = state; } void SetDriveError(DriveError error) { - s_error_code = error; + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + state.error_code = error; } void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type, s64 cycles_late, const std::vector& data) { + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + // The data parameter contains the requested data iff this was called from DVDThread, and is // empty otherwise. DVDThread is the only source of ReplyType::NoReply and ReplyType::DTK. @@ -1349,12 +1401,12 @@ void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type if (reply_type == ReplyType::NoReply) transfer_size = static_cast(data.size()); else if (reply_type == ReplyType::Interrupt || reply_type == ReplyType::IOS) - transfer_size = s_DILENGTH; + transfer_size = state.DILENGTH; if (interrupt_type == DIInterruptType::TCINT) { - s_DIMAR += transfer_size; - s_DILENGTH -= transfer_size; + state.DIMAR += transfer_size; + state.DILENGTH -= transfer_size; } switch (reply_type) @@ -1366,9 +1418,9 @@ void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type case ReplyType::Interrupt: { - if (s_DICR.TSTART) + if (state.DICR.TSTART) { - s_DICR.TSTART = 0; + state.DICR.TSTART = 0; GenerateDIInterrupt(interrupt_type); } break; @@ -1393,6 +1445,8 @@ void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type static void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& partition, u32 output_address, ReplyType reply_type) { + auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData(); + // The drive continues to read 1 MiB beyond the last read position when idle. // If a future read falls within this window, part of the read may be returned // from the buffer. Data can be transferred from the buffer at up to 32 MiB/s. @@ -1434,33 +1488,34 @@ static void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& parti } else { - if (s_read_buffer_start_time == s_read_buffer_end_time) + if (state.read_buffer_start_time == state.read_buffer_end_time) { // No buffer buffer_start = buffer_end = head_position = 0; } else { - buffer_start = s_read_buffer_end_offset > STREAMING_BUFFER_SIZE ? - s_read_buffer_end_offset - STREAMING_BUFFER_SIZE : + buffer_start = state.read_buffer_end_offset > STREAMING_BUFFER_SIZE ? + state.read_buffer_end_offset - STREAMING_BUFFER_SIZE : 0; DEBUG_LOG_FMT(DVDINTERFACE, "Buffer: now={:#x} start time={:#x} end time={:#x}", current_time, - s_read_buffer_start_time, s_read_buffer_end_time); + state.read_buffer_start_time, state.read_buffer_end_time); - if (current_time >= s_read_buffer_end_time) + if (current_time >= state.read_buffer_end_time) { // Buffer is fully read - buffer_end = s_read_buffer_end_offset; + buffer_end = state.read_buffer_end_offset; } else { // The amount of data the buffer contains *right now*, rounded to a DVD ECC block. - buffer_end = s_read_buffer_start_offset + - Common::AlignDown((current_time - s_read_buffer_start_time) * - (s_read_buffer_end_offset - s_read_buffer_start_offset) / - (s_read_buffer_end_time - s_read_buffer_start_time), - DVD_ECC_BLOCK_SIZE); + buffer_end = + state.read_buffer_start_offset + + Common::AlignDown((current_time - state.read_buffer_start_time) * + (state.read_buffer_end_offset - state.read_buffer_start_offset) / + (state.read_buffer_end_time - state.read_buffer_start_time), + DVD_ECC_BLOCK_SIZE); } head_position = buffer_end; @@ -1573,36 +1628,37 @@ static void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& parti } else { - // Note that the s_read_buffer_start_offset value we calculate here is not the + // Note that the read_buffer_start_offset value we calculate here is not the // actual start of the buffer - it is just the start of the portion we need to read. - // The actual start of the buffer is s_read_buffer_end_offset - STREAMING_BUFFER_SIZE. + // The actual start of the buffer is read_buffer_end_offset - STREAMING_BUFFER_SIZE. if (last_block >= buffer_end) { // Full buffer read - s_read_buffer_start_offset = last_block; + state.read_buffer_start_offset = last_block; } else { // Partial buffer read - s_read_buffer_start_offset = buffer_end; + state.read_buffer_start_offset = buffer_end; } - s_read_buffer_end_offset = last_block + STREAMING_BUFFER_SIZE - BUFFER_BACKWARD_SEEK_LIMIT; + state.read_buffer_end_offset = last_block + STREAMING_BUFFER_SIZE - BUFFER_BACKWARD_SEEK_LIMIT; if (seek) { // If we seek, the block preceding the first accessed block never gets read into the buffer - s_read_buffer_end_offset = - std::max(s_read_buffer_end_offset, first_block + STREAMING_BUFFER_SIZE); + state.read_buffer_end_offset = + std::max(state.read_buffer_end_offset, first_block + STREAMING_BUFFER_SIZE); } // Assume the buffer starts prefetching new blocks right after the end of the last operation - s_read_buffer_start_time = current_time + ticks_until_completion; - s_read_buffer_end_time = - s_read_buffer_start_time + + state.read_buffer_start_time = current_time + ticks_until_completion; + state.read_buffer_end_time = + state.read_buffer_start_time + static_cast(ticks_per_second * - DVDMath::CalculateRawDiscReadTime( - s_read_buffer_start_offset, - s_read_buffer_end_offset - s_read_buffer_start_offset, wii_disc)); + DVDMath::CalculateRawDiscReadTime(state.read_buffer_start_offset, + state.read_buffer_end_offset - + state.read_buffer_start_offset, + wii_disc)); } DEBUG_LOG_FMT(DVDINTERFACE, diff --git a/Source/Core/Core/HW/DVD/DVDInterface.h b/Source/Core/Core/HW/DVD/DVDInterface.h index 930da55b12..ef619f0f65 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.h +++ b/Source/Core/Core/HW/DVD/DVDInterface.h @@ -23,6 +23,23 @@ class Mapping; namespace DVDInterface { +class DVDInterfaceState +{ +public: + DVDInterfaceState(); + DVDInterfaceState(const DVDInterfaceState&) = delete; + DVDInterfaceState(DVDInterfaceState&&) = delete; + DVDInterfaceState& operator=(const DVDInterfaceState&) = delete; + DVDInterfaceState& operator=(DVDInterfaceState&&) = delete; + ~DVDInterfaceState(); + + struct Data; + Data& GetData() { return *m_data; } + +private: + std::unique_ptr m_data; +}; + enum class DICommand : u8 { Inquiry = 0x12, diff --git a/Source/Core/Core/System.cpp b/Source/Core/Core/System.cpp index aa257af6f3..108209b024 100644 --- a/Source/Core/Core/System.cpp +++ b/Source/Core/Core/System.cpp @@ -7,6 +7,7 @@ #include "AudioCommon/SoundStream.h" #include "Core/Config/MainSettings.h" +#include "Core/HW/DVD/DVDInterface.h" #include "Core/HW/DVD/DVDThread.h" namespace Core @@ -17,6 +18,7 @@ struct System::Impl bool m_sound_stream_running = false; bool m_audio_dump_started = false; + DVDInterface::DVDInterfaceState m_dvd_interface_state; DVDThread::DVDThreadState m_dvd_thread_state; }; @@ -62,6 +64,11 @@ void System::SetAudioDumpStarted(bool started) m_impl->m_audio_dump_started = started; } +DVDInterface::DVDInterfaceState& System::GetDVDInterfaceState() const +{ + return m_impl->m_dvd_interface_state; +} + DVDThread::DVDThreadState& System::GetDVDThreadState() const { return m_impl->m_dvd_thread_state; diff --git a/Source/Core/Core/System.h b/Source/Core/Core/System.h index 659caae914..704d3dca71 100644 --- a/Source/Core/Core/System.h +++ b/Source/Core/Core/System.h @@ -7,6 +7,10 @@ class SoundStream; +namespace DVDInterface +{ +class DVDInterfaceState; +} namespace DVDThread { class DVDThreadState; @@ -45,6 +49,7 @@ public: bool IsAudioDumpStarted() const; void SetAudioDumpStarted(bool started); + DVDInterface::DVDInterfaceState& GetDVDInterfaceState() const; DVDThread::DVDThreadState& GetDVDThreadState() const; private: