diff --git a/Source/Core/Core/HW/DSPHLE/DSPHLE.cpp b/Source/Core/Core/HW/DSPHLE/DSPHLE.cpp index 1176568d7b..c686930383 100644 --- a/Source/Core/Core/HW/DSPHLE/DSPHLE.cpp +++ b/Source/Core/Core/HW/DSPHLE/DSPHLE.cpp @@ -7,6 +7,7 @@ #include "Common/CommonTypes.h" #include "Common/MsgHandler.h" #include "Core/Core.h" +#include "Core/CoreTiming.h" #include "Core/HW/DSPHLE/UCodes/UCodes.h" #include "Core/HW/SystemTimers.h" @@ -104,6 +105,7 @@ void DSPHLE::DoState(PointerWrap& p) } p.DoPOD(m_dsp_control); + p.Do(m_control_reg_init_code_clear_time); p.DoPOD(m_dsp_state); int ucode_crc = UCodeInterface::GetCRC(m_ucode.get()); @@ -190,16 +192,33 @@ u16 DSPHLE::DSP_WriteControlRegister(u16 value) { DSP::UDSPControl temp(value); + if (m_dsp_control.DSPHalt != temp.DSPHalt) + { + INFO_LOG_FMT(DSPHLE, "DSP_CONTROL halt bit changed: {:04x} -> {:04x}", m_dsp_control.Hex, + value); + } + if (temp.DSPReset) { SetUCode(UCODE_ROM); temp.DSPReset = 0; } - if (temp.DSPInit == 0) + + // init - unclear if writing DSPInitCode does something. Clearing DSPInit immediately sets + // DSPInitCode, which gets unset a bit later... + if ((m_dsp_control.DSPInit != 0) && (temp.DSPInit == 0)) { - // copy 128 byte from ARAM 0x000000 to IMEM + // Copy 1024(?) bytes of uCode from main memory 0x81000000 (or is it ARAM 00000000?) + // to IMEM 0000 and jump to that code + // TODO: Determine exactly how this initialization works + // We could hash the input data, but this is only used for initialization purposes on licensed + // games and by devkitpro, so there's no real point in doing so. + // Datel has similar logic to retail games, but they clear bit 0x80 (DSP) instead of bit 0x800 + // (DSPInit) so they end up not using the init uCode. SetUCode(UCODE_INIT_AUDIO_SYSTEM); - temp.DSPInitCode = 0; + temp.DSPInitCode = 1; + // Number obtained from real hardware on a Wii, but it's not perfectly consistent + m_control_reg_init_code_clear_time = SystemTimers::GetFakeTimeBase() + 130; } m_dsp_control.Hex = temp.Hex; @@ -208,6 +227,13 @@ u16 DSPHLE::DSP_WriteControlRegister(u16 value) u16 DSPHLE::DSP_ReadControlRegister() { + if (m_dsp_control.DSPInitCode != 0) + { + if (SystemTimers::GetFakeTimeBase() >= m_control_reg_init_code_clear_time) + m_dsp_control.DSPInitCode = 0; + else + CoreTiming::ForceExceptionCheck(50); // Keep checking + } return m_dsp_control.Hex; } diff --git a/Source/Core/Core/HW/DSPHLE/DSPHLE.h b/Source/Core/Core/HW/DSPHLE/DSPHLE.h index af684655d9..997529c64e 100644 --- a/Source/Core/Core/HW/DSPHLE/DSPHLE.h +++ b/Source/Core/Core/HW/DSPHLE/DSPHLE.h @@ -65,6 +65,7 @@ private: std::unique_ptr m_last_ucode; DSP::UDSPControl m_dsp_control; + u64 m_control_reg_init_code_clear_time = 0; CMailHandler m_mail_handler; }; } // namespace DSP::HLE