diff --git a/Source/Core/Core/DSP/DSPCore.cpp b/Source/Core/Core/DSP/DSPCore.cpp index e20d570fcc..dadc8effa6 100644 --- a/Source/Core/Core/DSP/DSPCore.cpp +++ b/Source/Core/Core/DSP/DSPCore.cpp @@ -161,7 +161,7 @@ bool SDSP::Initialize(const DSPInitOptions& opts) r.sr |= SR_INT_ENABLE; r.sr |= SR_EXT_INT_ENABLE; - cr = CR_INIT | CR_HALT; + control_reg = CR_INIT | CR_HALT; InitializeIFX(); // Mostly keep IRAM write protected. We unprotect only when DMA-ing // in new ucodes. @@ -210,7 +210,7 @@ void SDSP::CheckExternalInterrupt() // Signal the SPU about new mail SetException(ExceptionType::ExternalInterrupt); - cr &= ~CR_EXTERNAL_INT; + control_reg &= ~CR_EXTERNAL_INT; } void SDSP::CheckExceptions() @@ -378,7 +378,8 @@ void SDSP::DoState(PointerWrap& p) { p.Do(r); p.Do(pc); - p.Do(cr); + p.Do(control_reg); + p.Do(control_reg_init_code_clear_time); p.Do(reg_stack_ptrs); p.Do(exceptions); p.Do(external_interrupt_waiting); diff --git a/Source/Core/Core/DSP/DSPCore.h b/Source/Core/Core/DSP/DSPCore.h index 4c293fecf7..a12f49270d 100644 --- a/Source/Core/Core/DSP/DSPCore.h +++ b/Source/Core/Core/DSP/DSPCore.h @@ -419,11 +419,12 @@ struct SDSP DSP_Regs r{}; u16 pc = 0; - // This is NOT the same cr as r.cr. + // This is NOT the same as r.cr. // This register is shared with the main emulation, see DSP.cpp // The engine has control over 0x0C07 of this reg. // Bits are defined in a struct in DSP.cpp. - u16 cr = 0; + u16 control_reg = 0; + u64 control_reg_init_code_clear_time = 0; u8 reg_stack_ptrs[4]{}; u8 exceptions = 0; // pending exceptions @@ -577,9 +578,6 @@ public: Interpreter::Interpreter& GetInterpreter() { return *m_dsp_interpreter; } const Interpreter::Interpreter& GetInterpreter() const { return *m_dsp_interpreter; } - bool GetInitHax() const { return m_init_hax; } - void SetInitHax(bool value) { m_init_hax = value; } - private: SDSP m_dsp; DSPBreakpoints m_dsp_breakpoints; diff --git a/Source/Core/Core/DSP/DSPHWInterface.cpp b/Source/Core/Core/DSP/DSPHWInterface.cpp index 1f22ed7661..3866dd1bde 100644 --- a/Source/Core/Core/DSP/DSPHWInterface.cpp +++ b/Source/Core/Core/DSP/DSPHWInterface.cpp @@ -39,16 +39,9 @@ u16 SDSP::ReadMailboxLow(Mailbox mailbox) const u32 value = GetMailbox(mailbox).load(std::memory_order_acquire); GetMailbox(mailbox).store(value & ~0x80000000, std::memory_order_release); - if (m_dsp_core.GetInitHax() && mailbox == Mailbox::DSP) - { - m_dsp_core.SetInitHax(false); - m_dsp_core.Reset(); - return 0x4348; - } - #if defined(_DEBUG) || defined(DEBUGFAST) const char* const type = mailbox == Mailbox::DSP ? "DSP" : "CPU"; - DEBUG_LOG_FMT(DSP_MAIL, "{}(RM) B:{} M:0x{:#010x} (pc={:#06x})", type, static_cast(mailbox), + DEBUG_LOG_FMT(DSP_MAIL, "{}(RM) B:{} M:{:#010x} (pc={:#06x})", type, static_cast(mailbox), PeekMailbox(mailbox), pc); #endif @@ -57,11 +50,6 @@ u16 SDSP::ReadMailboxLow(Mailbox mailbox) u16 SDSP::ReadMailboxHigh(Mailbox mailbox) { - if (m_dsp_core.GetInitHax() && mailbox == Mailbox::DSP) - { - return 0x8054; - } - // TODO: mask away the top bit? return static_cast(PeekMailbox(mailbox) >> 16); } diff --git a/Source/Core/Core/DSP/Interpreter/DSPIntBranch.cpp b/Source/Core/Core/DSP/Interpreter/DSPIntBranch.cpp index 8b2aca8c8b..79df56479e 100644 --- a/Source/Core/Core/DSP/Interpreter/DSPIntBranch.cpp +++ b/Source/Core/Core/DSP/Interpreter/DSPIntBranch.cpp @@ -127,7 +127,7 @@ void Interpreter::rti(const UDSPInstruction opc) void Interpreter::halt(const UDSPInstruction) { auto& state = m_dsp_core.DSPState(); - state.cr |= CR_HALT; + state.control_reg |= CR_HALT; state.pc--; } diff --git a/Source/Core/Core/DSP/Interpreter/DSPInterpreter.cpp b/Source/Core/Core/DSP/Interpreter/DSPInterpreter.cpp index a2332320c8..dfc351a18a 100644 --- a/Source/Core/Core/DSP/Interpreter/DSPInterpreter.cpp +++ b/Source/Core/Core/DSP/Interpreter/DSPInterpreter.cpp @@ -7,12 +7,17 @@ #include "Common/Assert.h" #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" +#include "Common/MemoryUtil.h" +#include "Core/CoreTiming.h" #include "Core/DSP/DSPAnalyzer.h" #include "Core/DSP/DSPCore.h" +#include "Core/DSP/DSPHost.h" #include "Core/DSP/DSPTables.h" #include "Core/DSP/Interpreter/DSPIntCCUtil.h" #include "Core/DSP/Interpreter/DSPIntTables.h" +#include "Core/HW/Memmap.h" +#include "Core/HW/SystemTimers.h" namespace DSP::Interpreter { @@ -81,7 +86,7 @@ int Interpreter::RunCyclesThread(int cycles) while (true) { - if ((state.cr & CR_HALT) != 0) + if ((state.control_reg & CR_HALT) != 0) return 0; if (state.external_interrupt_waiting.exchange(false, std::memory_order_acquire)) @@ -104,7 +109,7 @@ int Interpreter::RunCyclesDebug(int cycles) // First, let's run a few cycles with no idle skipping so that things can progress a bit. for (int i = 0; i < 8; i++) { - if ((state.cr & CR_HALT) != 0) + if ((state.control_reg & CR_HALT) != 0) return 0; if (m_dsp_core.BreakPoints().IsAddressBreakPoint(state.pc)) @@ -124,7 +129,7 @@ int Interpreter::RunCyclesDebug(int cycles) // idle loops. for (int i = 0; i < 8; i++) { - if ((state.cr & CR_HALT) != 0) + if ((state.control_reg & CR_HALT) != 0) return 0; if (m_dsp_core.BreakPoints().IsAddressBreakPoint(state.pc)) @@ -169,7 +174,7 @@ int Interpreter::RunCycles(int cycles) // progress a bit. for (int i = 0; i < 8; i++) { - if ((state.cr & CR_HALT) != 0) + if ((state.control_reg & CR_HALT) != 0) return 0; Step(); @@ -185,7 +190,7 @@ int Interpreter::RunCycles(int cycles) // idle loops. for (int i = 0; i < 8; i++) { - if ((state.cr & CR_HALT) != 0) + if ((state.control_reg & CR_HALT) != 0) return 0; if (state.GetAnalyzer().IsIdleSkip(state.pc)) @@ -212,8 +217,19 @@ int Interpreter::RunCycles(int cycles) } // NOTE: These have nothing to do with SDSP::r::cr! -void Interpreter::WriteCR(u16 val) +void Interpreter::WriteControlRegister(u16 val) { + auto& state = m_dsp_core.DSPState(); + + if ((state.control_reg & CR_HALT) != (val & CR_HALT)) + { + // This bit is handled by Interpreter::RunCycles and DSPEmitter::CompileDispatcher + INFO_LOG_FMT(DSPLLE, "DSP_CONTROL halt bit changed: {:04x} -> {:04x}, PC {:04x}", + state.control_reg, val, state.pc); + } + + // The CR_EXTERNAL_INT bit is handled by DSPLLE::DSP_WriteControlRegister + // reset if ((val & CR_RESET) != 0) { @@ -221,34 +237,43 @@ void Interpreter::WriteCR(u16 val) m_dsp_core.Reset(); val &= ~CR_RESET; } - // init - else if (val == CR_HALT) + // init - unclear if writing CR_INIT_CODE does something. Clearing CR_INIT immediately sets + // CR_INIT_CODE, which gets unset a bit later... + if (((state.control_reg & CR_INIT) != 0) && ((val & CR_INIT) == 0)) { - // HAX! - // OSInitAudioSystem ucode should send this mail - not DSP core itself INFO_LOG_FMT(DSPLLE, "DSP_CONTROL INIT"); - m_dsp_core.SetInitHax(true); - val |= CR_INIT; + // 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 + state.pc = 0; + + Common::UnWriteProtectMemory(state.iram, DSP_IRAM_BYTE_SIZE, false); + Host::DMAToDSP(state.iram, 0x81000000, 0x1000); + Common::WriteProtectMemory(state.iram, DSP_IRAM_BYTE_SIZE, false); + + Host::CodeLoaded(m_dsp_core, 0x81000000, 0x1000); + + val &= ~CR_INIT; + val |= CR_INIT_CODE; + // Number obtained from real hardware on a Wii, but it's not perfectly consistent + state.control_reg_init_code_clear_time = SystemTimers::GetFakeTimeBase() + 130; } // update cr - m_dsp_core.DSPState().cr = val; + state.control_reg = val; } -u16 Interpreter::ReadCR() +u16 Interpreter::ReadControlRegister() { auto& state = m_dsp_core.DSPState(); - - if ((state.pc & 0x8000) != 0) + if ((state.control_reg & CR_INIT_CODE) != 0) { - state.cr |= CR_INIT; + if (SystemTimers::GetFakeTimeBase() >= state.control_reg_init_code_clear_time) + state.control_reg &= ~CR_INIT_CODE; + else + CoreTiming::ForceExceptionCheck(50); // Keep checking } - else - { - state.cr &= ~CR_INIT; - } - - return state.cr; + return state.control_reg; } void Interpreter::SetSRFlag(u16 flag) diff --git a/Source/Core/Core/DSP/Interpreter/DSPInterpreter.h b/Source/Core/Core/DSP/Interpreter/DSPInterpreter.h index c6be4516b7..9fb5a9dd89 100644 --- a/Source/Core/Core/DSP/Interpreter/DSPInterpreter.h +++ b/Source/Core/Core/DSP/Interpreter/DSPInterpreter.h @@ -32,8 +32,8 @@ public: int RunCycles(int cycles); int RunCyclesDebug(int cycles); - void WriteCR(u16 val); - u16 ReadCR(); + void WriteControlRegister(u16 val); + u16 ReadControlRegister(); void SetSRFlag(u16 flag); bool IsSRFlagSet(u16 flag) const; diff --git a/Source/Core/Core/DSP/Jit/x64/DSPEmitter.cpp b/Source/Core/Core/DSP/Jit/x64/DSPEmitter.cpp index 4960c4c992..a8c5041dc8 100644 --- a/Source/Core/Core/DSP/Jit/x64/DSPEmitter.cpp +++ b/Source/Core/Core/DSP/Jit/x64/DSPEmitter.cpp @@ -443,7 +443,7 @@ void DSPEmitter::CompileDispatcher() } // Check for DSP halt - TEST(8, M_SDSP_cr(), Imm8(CR_HALT)); + TEST(8, M_SDSP_control_reg(), Imm8(CR_HALT)); FixupBranch _halt = J_CC(CC_NE); // Execute block. Cycles executed returned in EAX. @@ -484,9 +484,9 @@ Gen::OpArg DSPEmitter::M_SDSP_exceptions() return MDisp(R15, static_cast(offsetof(SDSP, exceptions))); } -Gen::OpArg DSPEmitter::M_SDSP_cr() +Gen::OpArg DSPEmitter::M_SDSP_control_reg() { - return MDisp(R15, static_cast(offsetof(SDSP, cr))); + return MDisp(R15, static_cast(offsetof(SDSP, control_reg))); } Gen::OpArg DSPEmitter::M_SDSP_external_interrupt_waiting() diff --git a/Source/Core/Core/DSP/Jit/x64/DSPEmitter.h b/Source/Core/Core/DSP/Jit/x64/DSPEmitter.h index 77f5eccb62..840c77cff8 100644 --- a/Source/Core/Core/DSP/Jit/x64/DSPEmitter.h +++ b/Source/Core/Core/DSP/Jit/x64/DSPEmitter.h @@ -292,7 +292,7 @@ private: // SDSP memory offset helpers Gen::OpArg M_SDSP_pc(); Gen::OpArg M_SDSP_exceptions(); - Gen::OpArg M_SDSP_cr(); + Gen::OpArg M_SDSP_control_reg(); Gen::OpArg M_SDSP_external_interrupt_waiting(); Gen::OpArg M_SDSP_r_st(size_t index); Gen::OpArg M_SDSP_reg_stack_ptrs(size_t index); diff --git a/Source/Core/Core/DSP/Jit/x64/DSPJitBranch.cpp b/Source/Core/Core/DSP/Jit/x64/DSPJitBranch.cpp index 7ba437484b..03f50fb715 100644 --- a/Source/Core/Core/DSP/Jit/x64/DSPJitBranch.cpp +++ b/Source/Core/Core/DSP/Jit/x64/DSPJitBranch.cpp @@ -304,7 +304,7 @@ void DSPEmitter::rti(const UDSPInstruction opc) // Stops execution of DSP code. Sets bit DSP_CR_HALT in register DREG_CR. void DSPEmitter::halt(const UDSPInstruction) { - OR(16, M_SDSP_cr(), Imm16(CR_HALT)); + OR(16, M_SDSP_control_reg(), Imm16(CR_HALT)); // g_dsp.pc = dsp_reg_load_stack(StackRegister::Call); dsp_reg_load_stack(StackRegister::Call); MOV(16, M_SDSP_pc(), R(DX)); diff --git a/Source/Core/Core/HW/DSP.cpp b/Source/Core/Core/HW/DSP.cpp index 5edc5314a5..1858aa5ce2 100644 --- a/Source/Core/Core/HW/DSP.cpp +++ b/Source/Core/Core/HW/DSP.cpp @@ -330,6 +330,7 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base) s_dspState.DSPReset = tmpControl.DSPReset; s_dspState.DSPAssertInt = tmpControl.DSPAssertInt; s_dspState.DSPHalt = tmpControl.DSPHalt; + s_dspState.DSPInitCode = tmpControl.DSPInitCode; s_dspState.DSPInit = tmpControl.DSPInit; // Interrupt (mask) @@ -346,7 +347,6 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base) s_dspState.DSP = 0; // unknown - s_dspState.DSPInitCode = tmpControl.DSPInitCode; s_dspState.pad = tmpControl.pad; if (s_dspState.pad != 0) { 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 diff --git a/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp b/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp index c555ff4b9a..5f66cbd5d2 100644 --- a/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp +++ b/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp @@ -183,7 +183,7 @@ void DSPLLE::Shutdown() u16 DSPLLE::DSP_WriteControlRegister(u16 value) { - m_dsp_core.GetInterpreter().WriteCR(value); + m_dsp_core.GetInterpreter().WriteControlRegister(value); if ((value & CR_EXTERNAL_INT) != 0) { @@ -207,7 +207,7 @@ u16 DSPLLE::DSP_WriteControlRegister(u16 value) u16 DSPLLE::DSP_ReadControlRegister() { - return m_dsp_core.GetInterpreter().ReadCR(); + return m_dsp_core.GetInterpreter().ReadControlRegister(); } u16 DSPLLE::DSP_ReadMailBoxHigh(bool cpu_mailbox) diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 9387adf72c..76349ba4d2 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -74,7 +74,7 @@ static std::recursive_mutex g_save_thread_mutex; static std::thread g_save_thread; // Don't forget to increase this after doing changes on the savestate system -constexpr u32 STATE_VERSION = 141; // Last changed in PR 8067 +constexpr u32 STATE_VERSION = 142; // Last changed in PR 10732 // Maps savestate versions to Dolphin versions. // Versions after 42 don't need to be added to this list,