diff --git a/Source/Project64-core/Logging.cpp b/Source/Project64-core/Logging.cpp index 558ed9e79..259bdd366 100644 --- a/Source/Project64-core/Logging.cpp +++ b/Source/Project64-core/Logging.cpp @@ -63,21 +63,7 @@ void CLogging::Log_LW(uint32_t PC, uint32_t VAddr) } if (VAddr >= 0xA4500000 && VAddr <= 0xA4500014) { - if (!LogAudioInterface()) - { - return; - } - g_MMU->LW_VAddr(VAddr, Value); - - switch (VAddr) - { - case 0xA4500000: LogMessage("%08X: read from AI_DRAM_ADDR_REG (%08X)", PC, Value); return; - case 0xA4500004: LogMessage("%08X: read from AI_LEN_REG (%08X)", PC, Value); return; - case 0xA4500008: LogMessage("%08X: read from AI_CONTROL_REG (%08X)", PC, Value); return; - case 0xA450000C: LogMessage("%08X: read from AI_STATUS_REG (%08X)", PC, Value); return; - case 0xA4500010: LogMessage("%08X: read from AI_DACRATE_REG (%08X)", PC, Value); return; - case 0xA4500014: LogMessage("%08X: read from AI_BITRATE_REG (%08X)", PC, Value); return; - } + return; } if (VAddr == 0xA4800000) { @@ -225,31 +211,12 @@ void CLogging::Log_SW(uint32_t PC, uint32_t VAddr, uint32_t Value) } } - if (VAddr >= 0xA4300000 && VAddr <= 0xA430000C) + if ((VAddr >= 0xA4300000 && VAddr <= 0xA430000C) || + (VAddr >= 0xA4400000 && VAddr <= 0xA4400034) || + (VAddr >= 0xA4500000 && VAddr <= 0xA4500014)) { return; } - if (VAddr >= 0xA4400000 && VAddr <= 0xA4400034) - { - return; - } - - if (VAddr >= 0xA4500000 && VAddr <= 0xA4500014) - { - if (!LogAudioInterface()) - { - return; - } - switch (VAddr) - { - case 0xA4500000: LogMessage("%08X: Writing 0x%08X to AI_DRAM_ADDR_REG", PC, Value); return; - case 0xA4500004: LogMessage("%08X: Writing 0x%08X to AI_LEN_REG", PC, Value); return; - case 0xA4500008: LogMessage("%08X: Writing 0x%08X to AI_CONTROL_REG", PC, Value); return; - case 0xA450000C: LogMessage("%08X: Writing 0x%08X to AI_STATUS_REG", PC, Value); return; - case 0xA4500010: LogMessage("%08X: Writing 0x%08X to AI_DACRATE_REG", PC, Value); return; - case 0xA4500014: LogMessage("%08X: Writing 0x%08X to AI_BITRATE_REG", PC, Value); return; - } - } if (VAddr == 0xA4800000) { diff --git a/Source/Project64-core/N64System/MemoryHandler/AudioInterfaceHandler.cpp b/Source/Project64-core/N64System/MemoryHandler/AudioInterfaceHandler.cpp new file mode 100644 index 000000000..1ffbe9f4d --- /dev/null +++ b/Source/Project64-core/N64System/MemoryHandler/AudioInterfaceHandler.cpp @@ -0,0 +1,196 @@ +#include "stdafx.h" +#include "AudioInterfaceHandler.h" +#include +#include +#include + +AudioInterfaceReg::AudioInterfaceReg(uint32_t * _AudioInterface) : + AI_DRAM_ADDR_REG(_AudioInterface[0]), + AI_LEN_REG(_AudioInterface[1]), + AI_CONTROL_REG(_AudioInterface[2]), + AI_STATUS_REG(_AudioInterface[3]), + AI_DACRATE_REG(_AudioInterface[4]), + AI_BITRATE_REG(_AudioInterface[5]) +{ +} + +AudioInterfaceHandler::AudioInterfaceHandler(CN64System & System, CRegisters & Reg) : + AudioInterfaceReg(Reg.m_Audio_Interface), + m_System(System), + m_Reg(Reg), + m_Plugins(System.GetPlugins()), + m_PC(Reg.m_PROGRAM_COUNTER) +{ + System.RegisterCallBack(CN64SystemCB_Reset, this, (CN64System::CallBackFunction)stSystemReset); + System.RegisterCallBack(CN64SystemCB_LoadedGameState, this, (CN64System::CallBackFunction)stLoadedGameState); +} + +AudioInterfaceHandler::~AudioInterfaceHandler() +{ + m_System.UnregisterCallBack(CN64SystemCB_Reset, this, (CN64System::CallBackFunction)stSystemReset); + m_System.UnregisterCallBack(CN64SystemCB_LoadedGameState, this, (CN64System::CallBackFunction)stLoadedGameState); +} + +bool AudioInterfaceHandler::Read32(uint32_t Address, uint32_t & Value) +{ + switch (Address & 0x1FFFFFFF) + { + case 0x04500004: + if (bFixedAudio()) + { + Value = m_Audio.GetLength(); + } + else + { + if (m_Plugins->Audio()->AiReadLength != nullptr) + { + Value = m_Plugins->Audio()->AiReadLength(); + } + else + { + Value = 0; + } + } + break; + case 0x0450000C: + if (bFixedAudio()) + { + Value = m_Audio.GetStatus(); + } + else + { + Value = AI_STATUS_REG; + } + break; + default: + Value = 0; + if (HaveDebugger()) + { + g_Notify->BreakPoint(__FILE__, __LINE__); + } + } + + if (GenerateLog() && LogAudioInterface()) + { + switch (Address & 0x1FFFFFFF) + { + case 0x04500000: LogMessage("%08X: read from AI_DRAM_ADDR_REG (%08X)", m_PC, Value); break; + case 0x04500004: LogMessage("%08X: read from AI_LEN_REG (%08X)", m_PC, Value); break; + case 0x04500008: LogMessage("%08X: read from AI_CONTROL_REG (%08X)", m_PC, Value); break; + case 0x0450000C: LogMessage("%08X: read from AI_STATUS_REG (%08X)", m_PC, Value); break; + case 0x04500010: LogMessage("%08X: read from AI_DACRATE_REG (%08X)", m_PC, Value); break; + case 0x04500014: LogMessage("%08X: read from AI_BITRATE_REG (%08X)", m_PC, Value); break; + default: + if (HaveDebugger()) + { + g_Notify->BreakPoint(__FILE__, __LINE__); + } + } + } + return true; +} + +bool AudioInterfaceHandler::Write32(uint32_t Address, uint32_t Value, uint32_t Mask) +{ + if (GenerateLog() && LogAudioInterface()) + { + switch (Address & 0x1FFFFFFF) + { + case 0x04500000: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to AI_DRAM_ADDR_REG", m_PC, Value, Mask); break; + case 0x04500004: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to AI_LEN_REG", m_PC, Value, Mask); break; + case 0x04500008: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to AI_CONTROL_REG", m_PC, Value, Mask); break; + case 0x0450000C: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to AI_STATUS_REG", m_PC, Value, Mask); break; + case 0x04500010: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to AI_DACRATE_REG", m_PC, Value, Mask); break; + case 0x04500014: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to AI_BITRATE_REG", m_PC, Value, Mask); break; + default: + if (HaveDebugger()) + { + g_Notify->BreakPoint(__FILE__, __LINE__); + } + } + } + + uint32_t MaskedValue = Value & Mask; + switch (Address & 0x1FFFFFFF) + { + case 0x04500000: AI_DRAM_ADDR_REG = (AI_DRAM_ADDR_REG & ~Mask) | (MaskedValue); break; + case 0x04500004: + AI_LEN_REG = (AI_LEN_REG & ~Mask) | (MaskedValue); + if (bFixedAudio()) + { + m_Audio.LenChanged(); + } + else + { + if (m_Plugins->Audio()->AiLenChanged != nullptr) + { + m_Plugins->Audio()->AiLenChanged(); + } + } + break; + case 0x04500008: AI_CONTROL_REG = (MaskedValue & 1); break; + case 0x0450000C: + m_Reg.MI_INTR_REG &= ~MI_INTR_AI; + m_Reg.m_AudioIntrReg &= ~MI_INTR_AI; + m_Reg.CheckInterrupts(); + break; + case 0x04500010: + AI_DACRATE_REG = (AI_DACRATE_REG & ~Mask) | (MaskedValue); + m_Plugins->Audio()->DacrateChanged(m_System.SystemType()); + if (bFixedAudio()) + { + m_Audio.SetFrequency(AI_DACRATE_REG, m_System.SystemType()); + } + break; + case 0x04500014: AI_DACRATE_REG = (AI_BITRATE_REG & ~Mask) | (MaskedValue); break; + default: + if (HaveDebugger()) + { + g_Notify->BreakPoint(__FILE__, __LINE__); + } + } + return true; +} + +void AudioInterfaceHandler::TimerInterrupt(void) +{ + m_Audio.InterruptTimerDone(); +} + +void AudioInterfaceHandler::TimerBusy(void) +{ + m_Audio.BusyTimerDone(); +} + +void AudioInterfaceHandler::SetViIntr(uint32_t VI_INTR_TIME) +{ + m_Audio.SetViIntr(VI_INTR_TIME); +} + +void AudioInterfaceHandler::SetFrequency(uint32_t Dacrate, uint32_t System) +{ + m_Audio.SetFrequency(Dacrate, System); +} + +void AudioInterfaceHandler::LoadedGameState(void) +{ + if (bFixedAudio()) + { + m_Audio.SetFrequency(m_Reg.AI_DACRATE_REG, SystemType()); + } +} + +void AudioInterfaceHandler::SystemReset(void) +{ + m_Audio.Reset(); +} + +uint32_t AudioInterfaceHandler::GetLength(void) +{ + return m_Audio.GetLength(); +} + +uint32_t AudioInterfaceHandler::GetStatus(void) +{ + return m_Audio.GetStatus(); +} diff --git a/Source/Project64-core/N64System/MemoryHandler/AudioInterfaceHandler.h b/Source/Project64-core/N64System/MemoryHandler/AudioInterfaceHandler.h new file mode 100644 index 000000000..c59815ef9 --- /dev/null +++ b/Source/Project64-core/N64System/MemoryHandler/AudioInterfaceHandler.h @@ -0,0 +1,76 @@ +#pragma once +#include "MemoryHandler.h" +#include +#include +#include +#include +#include + +enum +{ + AI_STATUS_FIFO_FULL = 0x80000000, // Bit 31: Full + AI_STATUS_DMA_BUSY = 0x40000000, // Bit 30: Busy +}; + +class AudioInterfaceReg +{ +protected: + AudioInterfaceReg(uint32_t * _AudioInterface); + +public: + uint32_t & AI_DRAM_ADDR_REG; + uint32_t & AI_LEN_REG; + uint32_t & AI_CONTROL_REG; + uint32_t & AI_STATUS_REG; + uint32_t & AI_DACRATE_REG; + uint32_t & AI_BITRATE_REG; + +private: + AudioInterfaceReg(); + AudioInterfaceReg(const AudioInterfaceReg&); + AudioInterfaceReg& operator=(const AudioInterfaceReg&); +}; + +class CRegisters; +class CN64System; +class CPlugins; +class CAudio; + +class AudioInterfaceHandler : + public MemoryHandler, + public AudioInterfaceReg, + private CGameSettings, + private CDebugSettings, + private CLogging +{ +public: + AudioInterfaceHandler(CN64System & System, CRegisters & Reg); + ~AudioInterfaceHandler(); + + bool Read32(uint32_t Address, uint32_t & Value); + bool Write32(uint32_t Address, uint32_t Value, uint32_t Mask); + + void TimerInterrupt(void); + void TimerBusy(void); + void SetViIntr(uint32_t VI_INTR_TIME); + void SetFrequency(uint32_t Dacrate, uint32_t System); + uint32_t GetLength(); + uint32_t GetStatus(); + +private: + AudioInterfaceHandler(); + AudioInterfaceHandler(const AudioInterfaceHandler &); + AudioInterfaceHandler & operator=(const AudioInterfaceHandler &); + + static void stSystemReset(AudioInterfaceHandler * _this) { _this->SystemReset(); } + static void stLoadedGameState(AudioInterfaceHandler * _this) { _this->LoadedGameState(); } + + void LoadedGameState(void); + void SystemReset(void); + + CN64System & m_System; + CRegisters & m_Reg; + CPlugins * m_Plugins; + CAudio m_Audio; + uint32_t & m_PC; +}; diff --git a/Source/Project64-core/N64System/Mips/MemoryVirtualMem.cpp b/Source/Project64-core/N64System/Mips/MemoryVirtualMem.cpp index 010d7c114..ecf98d55b 100755 --- a/Source/Project64-core/N64System/Mips/MemoryVirtualMem.cpp +++ b/Source/Project64-core/N64System/Mips/MemoryVirtualMem.cpp @@ -21,20 +21,21 @@ uint32_t CMipsMemoryVM::RegModValue; #pragma warning(disable:4355) // Disable 'this' : used in base member initializer list -CMipsMemoryVM::CMipsMemoryVM(CN64System & System, CRegisters & Reg, bool SavesReadOnly) : +CMipsMemoryVM::CMipsMemoryVM(CN64System & System, bool SavesReadOnly) : CPifRam(SavesReadOnly), CFlashram(SavesReadOnly), CSram(SavesReadOnly), CDMA(*this, *this), - m_Reg(Reg), - m_RDRAMRegistersHandler(Reg), + m_Reg(System.m_Reg), + m_AudioInterfaceHandler(System, System.m_Reg), + m_RDRAMRegistersHandler(System.m_Reg), m_RomMapped(false), - m_DPCommandRegistersHandler(System, System.GetPlugins(), Reg), - m_MIPSInterfaceHandler(Reg), - m_PeripheralInterfaceHandler(*this, Reg), - m_RDRAMInterfaceHandler(Reg), - m_SPRegistersHandler(System, *this, Reg), - m_VideoInterfaceHandler(System, *this, Reg), + m_DPCommandRegistersHandler(System, System.GetPlugins(), System.m_Reg), + m_MIPSInterfaceHandler(System.m_Reg), + m_PeripheralInterfaceHandler(*this, System.m_Reg), + m_RDRAMInterfaceHandler(System.m_Reg), + m_SPRegistersHandler(System, *this, System.m_Reg), + m_VideoInterfaceHandler(System, *this, System.m_Reg), m_Rom(nullptr), m_RomSize(0), m_RomWrittenTo(false), @@ -645,7 +646,7 @@ bool CMipsMemoryVM::LW_NonMemory(uint32_t PAddr, uint32_t* Value) case 0x04100000: m_DPCommandRegistersHandler.Read32(PAddr, m_MemLookupValue.UW[0]); break; case 0x04300000: m_MIPSInterfaceHandler.Read32(PAddr, m_MemLookupValue.UW[0]); break; case 0x04400000: m_VideoInterfaceHandler.Read32(PAddr, m_MemLookupValue.UW[0]); break; - case 0x04500000: Load32AudioInterface(); break; + case 0x04500000: m_AudioInterfaceHandler.Read32(PAddr, m_MemLookupValue.UW[0]); break; case 0x04600000: m_PeripheralInterfaceHandler.Read32(PAddr, m_MemLookupValue.UW[0]); break; case 0x04700000: m_RDRAMInterfaceHandler.Read32(PAddr, m_MemLookupValue.UW[0]); break; case 0x04800000: Load32SerialInterface(); break; @@ -765,7 +766,7 @@ bool CMipsMemoryVM::SW_NonMemory(uint32_t PAddr, uint32_t Value) case 0x04100000: m_DPCommandRegistersHandler.Write32(PAddr, Value, 0xFFFFFFFF); break; case 0x04300000: m_MIPSInterfaceHandler.Write32(PAddr, Value, 0xFFFFFFFF); break; case 0x04400000: m_VideoInterfaceHandler.Write32(PAddr, Value, 0xFFFFFFFF); break; - case 0x04500000: Write32AudioInterface(); break; + case 0x04500000: m_AudioInterfaceHandler.Write32(PAddr, Value, 0xFFFFFFFF); break; case 0x04600000: m_PeripheralInterfaceHandler.Write32(PAddr, Value, 0xFFFFFFFF); break; case 0x04700000: m_RDRAMInterfaceHandler.Write32(PAddr, Value, 0xFFFFFFFF); break; case 0x04800000: Write32SerialInterface(); break; @@ -1085,46 +1086,6 @@ void CMipsMemoryVM::ChangeMiIntrMask() } } -void CMipsMemoryVM::Load32AudioInterface(void) -{ - switch (m_MemLookupAddress & 0x1FFFFFFF) - { - case 0x04500004: - if (g_System->bFixedAudio()) - { - m_MemLookupValue.UW[0] = g_Audio->GetLength(); - } - else - { - if (g_Plugins->Audio()->AiReadLength != nullptr) - { - m_MemLookupValue.UW[0] = g_Plugins->Audio()->AiReadLength(); - } - else - { - m_MemLookupValue.UW[0] = 0; - } - } - break; - case 0x0450000C: - if (g_System->bFixedAudio()) - { - m_MemLookupValue.UW[0] = g_Audio->GetStatus(); - } - else - { - m_MemLookupValue.UW[0] = g_Reg->AI_STATUS_REG; - } - break; - default: - m_MemLookupValue.UW[0] = 0; - if (HaveDebugger()) - { - g_Notify->BreakPoint(__FILE__, __LINE__); - } - } -} - void CMipsMemoryVM::Load32SerialInterface(void) { switch (m_MemLookupAddress & 0x1FFFFFFF) @@ -1284,49 +1245,6 @@ void CMipsMemoryVM::Load32Rom(void) } } -void CMipsMemoryVM::Write32AudioInterface(void) -{ - switch (m_MemLookupAddress & 0xFFFFFFF) - { - case 0x04500000: g_Reg->AI_DRAM_ADDR_REG = m_MemLookupValue.UW[0]; break; - case 0x04500004: - g_Reg->AI_LEN_REG = m_MemLookupValue.UW[0]; - if (g_System->bFixedAudio()) - { - g_Audio->LenChanged(); - } - else - { - if (g_Plugins->Audio()->AiLenChanged != nullptr) - { - g_Plugins->Audio()->AiLenChanged(); - } - } - break; - case 0x04500008: g_Reg->AI_CONTROL_REG = (m_MemLookupValue.UW[0] & 1); break; - case 0x0450000C: - // Clear interrupt - g_Reg->MI_INTR_REG &= ~MI_INTR_AI; - g_Reg->m_AudioIntrReg &= ~MI_INTR_AI; - g_Reg->CheckInterrupts(); - break; - case 0x04500010: - g_Reg->AI_DACRATE_REG = m_MemLookupValue.UW[0]; - g_Plugins->Audio()->DacrateChanged(g_System->SystemType()); - if (g_System->bFixedAudio()) - { - g_Audio->SetFrequency(m_MemLookupValue.UW[0], g_System->SystemType()); - } - break; - case 0x04500014: g_Reg->AI_BITRATE_REG = m_MemLookupValue.UW[0]; break; - default: - if (HaveDebugger()) - { - g_Notify->BreakPoint(__FILE__, __LINE__); - } - } -} - void CMipsMemoryVM::Write32SerialInterface(void) { switch (m_MemLookupAddress & 0xFFFFFFF) diff --git a/Source/Project64-core/N64System/Mips/MemoryVirtualMem.h b/Source/Project64-core/N64System/Mips/MemoryVirtualMem.h index cd55e87e5..d775b86b0 100644 --- a/Source/Project64-core/N64System/Mips/MemoryVirtualMem.h +++ b/Source/Project64-core/N64System/Mips/MemoryVirtualMem.h @@ -52,7 +52,7 @@ class CMipsMemoryVM : private CGameSettings { public: - CMipsMemoryVM(CN64System & System, CRegisters & Reg, bool SavesReadOnly); + CMipsMemoryVM(CN64System & System, bool SavesReadOnly); ~CMipsMemoryVM(); static void ReserveMemory(); @@ -113,6 +113,7 @@ public: // Labels const char * LabelName(uint32_t Address) const; + AudioInterfaceHandler & AudioInterface(void) { return m_AudioInterfaceHandler; } VideoInterfaceHandler & VideoInterface(void) { return m_VideoInterfaceHandler; } private: @@ -139,7 +140,6 @@ private: bool SH_NonMemory(uint32_t PAddr, uint16_t Value); bool SW_NonMemory(uint32_t PAddr, uint32_t Value); - static void Load32AudioInterface(void); static void Load32SerialInterface(void); static void Load32CartridgeDomain1Address1(void); static void Load32CartridgeDomain1Address3(void); @@ -148,7 +148,6 @@ private: static void Load32PifRam(void); static void Load32Rom(void); - static void Write32AudioInterface(void); static void Write32SerialInterface(void); static void Write32CartridgeDomain2Address1(void); static void Write32CartridgeDomain2Address2(void); @@ -179,6 +178,7 @@ private: static uint8_t * m_Reserve1, *m_Reserve2; CRegisters & m_Reg; + AudioInterfaceHandler m_AudioInterfaceHandler; DisplayControlRegHandler m_DPCommandRegistersHandler; MIPSInterfaceHandler m_MIPSInterfaceHandler; PeripheralInterfaceHandler m_PeripheralInterfaceHandler; diff --git a/Source/Project64-core/N64System/Mips/Register.cpp b/Source/Project64-core/N64System/Mips/Register.cpp index eb6137531..dadaaf7a7 100644 --- a/Source/Project64-core/N64System/Mips/Register.cpp +++ b/Source/Project64-core/N64System/Mips/Register.cpp @@ -75,16 +75,6 @@ CP0registers::CP0registers(uint32_t * _CP0) : { } -AudioInterfaceReg::AudioInterfaceReg(uint32_t * _AudioInterface) : - AI_DRAM_ADDR_REG(_AudioInterface[0]), - AI_LEN_REG(_AudioInterface[1]), - AI_CONTROL_REG(_AudioInterface[2]), - AI_STATUS_REG(_AudioInterface[3]), - AI_DACRATE_REG(_AudioInterface[4]), - AI_BITRATE_REG(_AudioInterface[5]) -{ -} - DisplayControlReg::DisplayControlReg(uint32_t * _DisplayProcessor) : DPC_START_REG(_DisplayProcessor[0]), DPC_END_REG(_DisplayProcessor[1]), diff --git a/Source/Project64-core/N64System/Mips/Register.h b/Source/Project64-core/N64System/Mips/Register.h index f0b72dec1..37000c225 100644 --- a/Source/Project64-core/N64System/Mips/Register.h +++ b/Source/Project64-core/N64System/Mips/Register.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -163,32 +164,6 @@ enum MI_INTR_DP = 0x20, // Bit 5: DP INTR }; -// Audio interface registers -class AudioInterfaceReg -{ -protected: - AudioInterfaceReg (uint32_t * _AudioInterface); - -public: - uint32_t & AI_DRAM_ADDR_REG; - uint32_t & AI_LEN_REG; - uint32_t & AI_CONTROL_REG; - uint32_t & AI_STATUS_REG; - uint32_t & AI_DACRATE_REG; - uint32_t & AI_BITRATE_REG; - -private: - AudioInterfaceReg(); - AudioInterfaceReg(const AudioInterfaceReg&); - AudioInterfaceReg& operator=(const AudioInterfaceReg&); -}; - -enum -{ - AI_STATUS_FIFO_FULL = 0x80000000, // Bit 31: Full - AI_STATUS_DMA_BUSY = 0x40000000, // Bit 30: Busy -}; - // Signal processor interface flags enum { diff --git a/Source/Project64-core/N64System/Mips/SystemTiming.cpp b/Source/Project64-core/N64System/Mips/SystemTiming.cpp index 42d2bb01a..5485ae82f 100644 --- a/Source/Project64-core/N64System/Mips/SystemTiming.cpp +++ b/Source/Project64-core/N64System/Mips/SystemTiming.cpp @@ -4,14 +4,16 @@ #include #include #include +#include #include -CSystemTimer::CSystemTimer(CRegisters &Reg, int32_t & NextTimer) : +CSystemTimer::CSystemTimer(CRegisters &Reg, AudioInterfaceHandler & AudioInterface, int32_t & NextTimer) : m_LastUpdate(0), m_NextTimer(NextTimer), m_Current(UnknownTimer), m_inFixTimer(false), - m_Reg(Reg) + m_Reg(Reg), + m_AudioInterface(AudioInterface) { memset(m_TimerDetatils, 0, sizeof(m_TimerDetatils)); } @@ -251,11 +253,11 @@ void CSystemTimer::TimerDone() break; case CSystemTimer::AiTimerInterrupt: g_SystemTimer->StopTimer(CSystemTimer::AiTimerInterrupt); - g_Audio->InterruptTimerDone(); + m_AudioInterface.TimerInterrupt(); break; case CSystemTimer::AiTimerBusy: g_SystemTimer->StopTimer(CSystemTimer::AiTimerBusy); - g_Audio->BusyTimerDone(); + m_AudioInterface.TimerBusy(); break; default: g_Notify->BreakPoint(__FILE__, __LINE__); diff --git a/Source/Project64-core/N64System/Mips/SystemTiming.h b/Source/Project64-core/N64System/Mips/SystemTiming.h index c8a6aa3b4..6fc8411c9 100644 --- a/Source/Project64-core/N64System/Mips/SystemTiming.h +++ b/Source/Project64-core/N64System/Mips/SystemTiming.h @@ -5,6 +5,8 @@ #include #include +class AudioInterfaceHandler; + class CSystemTimer { public: @@ -37,7 +39,7 @@ public: int64_t CyclesToTimer; }; - CSystemTimer(CRegisters &Reg, int32_t & NextTimer); + CSystemTimer(CRegisters &Reg, AudioInterfaceHandler & AudioInterface, int32_t & NextTimer); void SetTimer(TimerType Type, uint32_t Cycles, bool bRelative); uint32_t GetTimer(TimerType Type); void StopTimer(TimerType Type); @@ -73,4 +75,5 @@ private: TimerType m_Current; bool m_inFixTimer; CRegisters & m_Reg; + AudioInterfaceHandler & m_AudioInterface; }; diff --git a/Source/Project64-core/N64System/N64System.cpp b/Source/Project64-core/N64System/N64System.cpp index a8897c8f9..f9bd846a6 100644 --- a/Source/Project64-core/N64System/N64System.cpp +++ b/Source/Project64-core/N64System/N64System.cpp @@ -30,14 +30,14 @@ CN64System::CN64System(CPlugins * Plugins, uint32_t randomizer_seed, bool SavesR m_Plugins(Plugins), m_SyncCPU(nullptr), m_SyncPlugins(nullptr), - m_MMU_VM(*this, m_Reg, SavesReadOnly), + m_MMU_VM(*this, SavesReadOnly), //m_Cheats(m_MMU_VM), m_TLB(this), m_Reg(this, this), m_Recomp(nullptr), m_InReset(false), m_NextTimer(0), - m_SystemTimer(m_Reg, m_NextTimer), + m_SystemTimer(m_Reg, m_MMU_VM.AudioInterface(), m_NextTimer), m_bCleanFrameBox(true), m_RspBroke(true), m_DMAUsed(false), @@ -190,7 +190,6 @@ void CN64System::UnregisterCallBack(CN64SystemCB Type, void * Data, CallBackFunc if (Callback != m_Callback.end()) { SETTING_CHANGED_CB_LIST & List = Callback->second; - bool found = false; for (SETTING_CHANGED_CB_LIST::const_iterator itr = List.begin(); itr != List.end(); itr++) { if (itr->Data == Data && itr->Func == Func) @@ -901,7 +900,6 @@ void CN64System::Reset(bool bInitReg, bool ClearMenory) WriteTrace(TraceN64System, TraceDebug, "Start (bInitReg: %s, ClearMenory: %s)", bInitReg ? "true" : "false", ClearMenory ? "true" : "false"); g_Settings->SaveBool(GameRunning_InReset, true); RefreshGameSettings(); - m_Audio.Reset(); m_MMU_VM.Reset(ClearMenory); m_CyclesToSkip = 0; @@ -983,7 +981,6 @@ bool CN64System::SetActiveSystem(bool bActive) g_TLB = &m_TLB; g_Reg = &m_Reg; g_Mempak = &m_Mempak; - g_Audio = &m_Audio; g_SystemTimer = &m_SystemTimer; g_TransVaddr = &m_MMU_VM; g_SystemEvents = this; @@ -1005,7 +1002,6 @@ bool CN64System::SetActiveSystem(bool bActive) g_MMU = nullptr; g_TLB = nullptr; g_Reg = nullptr; - g_Audio = nullptr; g_SystemTimer = nullptr; g_TransVaddr = nullptr; g_SystemEvents = nullptr; @@ -2248,11 +2244,6 @@ bool CN64System::LoadState(const char * FileName) m_Reg.MI_INTR_REG |= MI_INTR_AI; } - if (bFixedAudio()) - { - m_Audio.SetFrequency(m_Reg.AI_DACRATE_REG, SystemType()); - } - if (old_status != m_Reg.VI_STATUS_REG) { g_Plugins->Gfx()->ViStatusChanged(); @@ -2474,7 +2465,7 @@ void CN64System::RefreshScreen() g_SystemTimer->SetTimer(CSystemTimer::ViTimer, VI_INTR_TIME, true); if (bFixedAudio()) { - g_Audio->SetViIntr(VI_INTR_TIME); + m_MMU_VM.AudioInterface().SetViIntr(VI_INTR_TIME); } if (UpdateControllerOnRefresh() && g_Plugins->Control()->GetKeys != nullptr) { diff --git a/Source/Project64-core/N64System/N64System.h b/Source/Project64-core/N64System/N64System.h index 7c6afa174..5ef436628 100644 --- a/Source/Project64-core/N64System/N64System.h +++ b/Source/Project64-core/N64System/N64System.h @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -161,14 +160,13 @@ private: CPlugins * const m_Plugins; // The plugin container CPlugins * m_SyncPlugins; CN64System * m_SyncCPU; - CMipsMemoryVM m_MMU_VM; // Memory of the N64 + CMipsMemoryVM m_MMU_VM; CTLB m_TLB; CRegisters m_Reg; CMempak m_Mempak; CFramePerSecond m_FPS; CProfiling m_CPU_Usage; // Used to track the CPU usage CRecompiler * m_Recomp; - CAudio m_Audio; CSpeedLimiter m_Limiter; bool m_InReset; int32_t m_NextTimer; diff --git a/Source/Project64-core/N64System/Recompiler/x86/x86RecompilerOps.cpp b/Source/Project64-core/N64System/Recompiler/x86/x86RecompilerOps.cpp index a70d21b45..cd5d15e59 100644 --- a/Source/Project64-core/N64System/Recompiler/x86/x86RecompilerOps.cpp +++ b/Source/Project64-core/N64System/Recompiler/x86/x86RecompilerOps.cpp @@ -3234,78 +3234,32 @@ void CX86RecompilerOps::LW_KnownAddress(x86Reg Reg, uint32_t VAddr) Call_Direct((void *)((long**)(MemoryHandler *)&g_MMU->m_VideoInterfaceHandler)[0][0], "VideoInterfaceHandler::Read32"); #else PushImm32((uint32_t)&g_MMU->m_VideoInterfaceHandler); - Call_Direct(AddressOf(&SPRegistersHandler::Read32), "SPRegistersHandler::Read32"); + Call_Direct(AddressOf(&VideoInterfaceHandler::Read32), "VideoInterfaceHandler::Read32"); AddConstToX86Reg(x86_ESP, 16); #endif m_RegWorkingSet.AfterCallDirect(); MoveVariableToX86reg(&m_TempValue, "m_TempValue", Reg); } break; - case 0x04500000: // AI registers - switch (PAddr) + case 0x04500000: { - case 0x04500004: - if (g_System->bFixedAudio()) - { - m_RegWorkingSet.SetBlockCycleCount(m_RegWorkingSet.GetBlockCycleCount() - g_System->CountPerOp()); - UpdateCounters(m_RegWorkingSet, false, true); - m_RegWorkingSet.SetBlockCycleCount(m_RegWorkingSet.GetBlockCycleCount() + g_System->CountPerOp()); - m_RegWorkingSet.BeforeCallDirect(); -#ifdef _MSC_VER - MoveConstToX86reg((uint32_t)g_Audio, x86_ECX); - Call_Direct(AddressOf(&CAudio::GetLength), "CAudio::GetLength"); -#else - PushImm32((uint32_t)g_Audio); - Call_Direct(AddressOf(&CAudio::GetLength), "CAudio::GetLength"); - AddConstToX86Reg(x86_ESP, 4); -#endif - MoveX86regToVariable(x86_EAX, &m_TempValue, "m_TempValue"); - m_RegWorkingSet.AfterCallDirect(); - MoveVariableToX86reg(&m_TempValue, "m_TempValue", Reg); - } - else - { - if (g_Plugins->Audio()->AiReadLength != nullptr) - { - m_RegWorkingSet.BeforeCallDirect(); - Call_Direct((void *)g_Plugins->Audio()->AiReadLength, "AiReadLength"); - MoveX86regToVariable(x86_EAX, &m_TempValue, "m_TempValue"); - m_RegWorkingSet.AfterCallDirect(); - MoveVariableToX86reg(&m_TempValue, "m_TempValue", Reg); - } - else - { - MoveConstToX86reg(0, Reg); - } - } - break; - case 0x0450000C: - if (g_System->bFixedAudio()) - { - m_RegWorkingSet.BeforeCallDirect(); -#ifdef _MSC_VER - MoveConstToX86reg((uint32_t)g_Audio, x86_ECX); - Call_Direct(AddressOf(&CAudio::GetStatus), "GetStatus"); -#else - PushImm32((uint32_t)g_Audio); - Call_Direct(AddressOf(&CAudio::GetStatus), "GetStatus"); - AddConstToX86Reg(x86_ESP, 4); -#endif - MoveX86regToVariable(x86_EAX, &m_TempValue, "m_TempValue"); - m_RegWorkingSet.AfterCallDirect(); - MoveVariableToX86reg(&m_TempValue, "m_TempValue", Reg); - } - else - { - MoveVariableToX86reg(&g_Reg->AI_STATUS_REG, "AI_STATUS_REG", Reg); - } - break; - default: - MoveConstToX86reg(0, Reg); - if (ShowUnhandledMemory()) - { - g_Notify->DisplayError(stdstr_f("%s\nFailed to translate address: %08X", __FUNCTION__, VAddr).c_str()); - } + m_RegWorkingSet.SetBlockCycleCount(m_RegWorkingSet.GetBlockCycleCount() - g_System->CountPerOp()); + UpdateCounters(m_RegWorkingSet, false, true); + m_RegWorkingSet.SetBlockCycleCount(m_RegWorkingSet.GetBlockCycleCount() + g_System->CountPerOp()); + + m_RegWorkingSet.BeforeCallDirect(); + PushImm32("m_TempValue", (uint32_t)&m_TempValue); + PushImm32(PAddr & 0x1FFFFFFF); + #ifdef _MSC_VER + MoveConstToX86reg((uint32_t)(MemoryHandler *)&g_MMU->m_AudioInterfaceHandler, x86_ECX); + Call_Direct((void *)((long**)(MemoryHandler *)&g_MMU->m_AudioInterfaceHandler)[0][0], "AudioInterfaceHandler::Read32"); + #else + PushImm32((uint32_t)&g_MMU->m_AudioInterfaceHandler); + Call_Direct(AddressOf(&AudioInterfaceHandler::Read32), "AudioInterfaceHandler::Read32"); + AddConstToX86Reg(x86_ESP, 16); + #endif + m_RegWorkingSet.AfterCallDirect(); + MoveVariableToX86reg(&m_TempValue, "m_TempValue", Reg); } break; case 0x04600000: @@ -10660,7 +10614,7 @@ void CX86RecompilerOps::SW_Const(uint32_t Value, uint32_t VAddr) Call_Direct((void *)((long**)(MemoryHandler *)&g_MMU->m_VideoInterfaceHandler)[0][1], "VideoInterfaceHandler::Write32"); #else PushImm32((uint32_t)&g_MMU->m_VideoInterfaceHandler); - Call_Direct(AddressOf(&SPRegistersHandler::Read32), "SPRegistersHandler::Write32"); + Call_Direct(AddressOf(&m_VideoInterfaceHandler::Write32), "m_VideoInterfaceHandler::Write32"); AddConstToX86Reg(x86_ESP, 16); #endif m_RegWorkingSet.AfterCallDirect(); @@ -10731,54 +10685,24 @@ void CX86RecompilerOps::SW_Const(uint32_t Value, uint32_t VAddr) } } break; - case 0x04500000: // AI registers - switch (PAddr) - { - case 0x04500000: MoveConstToVariable(Value, &g_Reg->AI_DRAM_ADDR_REG, "AI_DRAM_ADDR_REG"); break; - case 0x04500004: - MoveConstToVariable(Value, &g_Reg->AI_LEN_REG, "AI_LEN_REG"); - m_RegWorkingSet.BeforeCallDirect(); - if (g_System->bFixedAudio()) - { - X86BreakPoint(__FILE__, __LINE__); - MoveConstToX86reg((uint32_t)g_Audio, x86_ECX); - Call_Direct(AddressOf(&CAudio::LenChanged), "LenChanged"); - } - else - { - Call_Direct((void *)g_Plugins->Audio()->AiLenChanged, "AiLenChanged"); - } - m_RegWorkingSet.AfterCallDirect(); - break; - case 0x04500008: MoveConstToVariable((Value & 1), &g_Reg->AI_CONTROL_REG, "AI_CONTROL_REG"); break; - case 0x0450000C: - // Clear interrupt - AndConstToVariable((uint32_t)~MI_INTR_AI, &g_Reg->MI_INTR_REG, "MI_INTR_REG"); - AndConstToVariable((uint32_t)~MI_INTR_AI, &g_Reg->m_AudioIntrReg, "m_AudioIntrReg"); - m_RegWorkingSet.BeforeCallDirect(); + case 0x04500000: + m_RegWorkingSet.SetBlockCycleCount(m_RegWorkingSet.GetBlockCycleCount() - g_System->CountPerOp()); + UpdateCounters(m_RegWorkingSet, false, true); + m_RegWorkingSet.SetBlockCycleCount(m_RegWorkingSet.GetBlockCycleCount() + g_System->CountPerOp()); + + m_RegWorkingSet.BeforeCallDirect(); + PushImm32(0xFFFFFFFF); + PushImm32(Value); + PushImm32(PAddr & 0x1FFFFFFF); #ifdef _MSC_VER - MoveConstToX86reg((uint32_t)g_Reg, x86_ECX); - Call_Direct(AddressOf(&CRegisters::CheckInterrupts), "CRegisters::CheckInterrupts"); + MoveConstToX86reg((uint32_t)(MemoryHandler *)&g_MMU->m_AudioInterfaceHandler, x86_ECX); + Call_Direct((void *)((long**)(MemoryHandler *)&g_MMU->m_AudioInterfaceHandler)[0][1], "AudioInterfaceHandler::Write32"); #else - PushImm32((uint32_t)g_Reg); - Call_Direct(AddressOf(&CRegisters::CheckInterrupts), "CRegisters::CheckInterrupts"); - AddConstToX86Reg(x86_ESP, 4); + PushImm32((uint32_t)&g_MMU->m_AudioInterfaceHandler); + Call_Direct(AddressOf(&AudioInterfaceHandler::Write32), "AudioInterfaceHandler::Write32"); + AddConstToX86Reg(x86_ESP, 16); #endif - m_RegWorkingSet.AfterCallDirect(); - break; - case 0x04500010: - sprintf(VarName, "RDRAM + %X", PAddr); - MoveConstToVariable(Value, PAddr + g_MMU->Rdram(), VarName); - break; - case 0x04500014: MoveConstToVariable(Value, &g_Reg->AI_BITRATE_REG, "AI_BITRATE_REG"); break; - default: - sprintf(VarName, "RDRAM + %X", PAddr); - MoveConstToVariable(Value, PAddr + g_MMU->Rdram(), VarName); - if (ShowUnhandledMemory()) - { - g_Notify->DisplayError(stdstr_f("%s\nTrying to store %08X in %08X?", __FUNCTION__, Value, VAddr).c_str()); - } - } + m_RegWorkingSet.AfterCallDirect(); break; case 0x04600000: switch (PAddr) @@ -11152,7 +11076,7 @@ void CX86RecompilerOps::SW_Register(x86Reg Reg, uint32_t VAddr) Call_Direct((void *)((long**)(MemoryHandler *)&g_MMU->m_VideoInterfaceHandler)[0][1], "VideoInterfaceHandler::Write32"); #else PushImm32((uint32_t)&g_MMU->m_VideoInterfaceHandler); - Call_Direct(AddressOf(&SPRegistersHandler::Read32), "SPRegistersHandler::Write32"); + Call_Direct(AddressOf(&VideoInterfaceHandler::Write32), "SPRegistersHandler::Write32"); AddConstToX86Reg(x86_ESP, 16); #endif m_RegWorkingSet.AfterCallDirect(); @@ -11230,62 +11154,23 @@ void CX86RecompilerOps::SW_Register(x86Reg Reg, uint32_t VAddr) } break; case 0x04500000: // AI registers - switch (PAddr) { - case 0x04500000: MoveX86regToVariable(Reg, &g_Reg->AI_DRAM_ADDR_REG, "AI_DRAM_ADDR_REG"); break; - case 0x04500004: - m_RegWorkingSet.SetBlockCycleCount(m_RegWorkingSet.GetBlockCycleCount() - g_System->CountPerOp()); - UpdateCounters(m_RegWorkingSet, false, true); - m_RegWorkingSet.SetBlockCycleCount(m_RegWorkingSet.GetBlockCycleCount() + g_System->CountPerOp()); - MoveX86regToVariable(Reg, &g_Reg->AI_LEN_REG, "AI_LEN_REG"); - m_RegWorkingSet.BeforeCallDirect(); - if (g_System->bFixedAudio()) - { + m_RegWorkingSet.SetBlockCycleCount(m_RegWorkingSet.GetBlockCycleCount() - g_System->CountPerOp()); + UpdateCounters(m_RegWorkingSet, false, true); + m_RegWorkingSet.SetBlockCycleCount(m_RegWorkingSet.GetBlockCycleCount() + g_System->CountPerOp()); + + m_RegWorkingSet.BeforeCallDirect(); + PushImm32(0xFFFFFFFF); + Push(Reg); + PushImm32(PAddr & 0x1FFFFFFF); #ifdef _MSC_VER - MoveConstToX86reg((uint32_t)g_Audio, x86_ECX); - Call_Direct(AddressOf(&CAudio::LenChanged), "LenChanged"); + MoveConstToX86reg((uint32_t)(MemoryHandler *)&g_MMU->m_AudioInterfaceHandler, x86_ECX); + Call_Direct((void *)((long**)(MemoryHandler *)&g_MMU->m_AudioInterfaceHandler)[0][1], "AudioInterfaceHandler::Write32"); #else - PushImm32((uint32_t)g_Audio); - Call_Direct(AddressOf(&CAudio::LenChanged), "LenChanged"); - AddConstToX86Reg(x86_ESP, 4); + PushImm32((uint32_t)&g_MMU->m_AudioInterfaceHandler); + Call_Direct(AddressOf(&AudioInterfaceHandler::Write32), "AudioInterfaceHandler::Write32"); + AddConstToX86Reg(x86_ESP, 16); #endif - } - else - { - Call_Direct((void *)g_Plugins->Audio()->AiLenChanged, "g_Plugins->Audio()->LenChanged"); - } - m_RegWorkingSet.AfterCallDirect(); - break; - case 0x04500008: - MoveX86regToVariable(Reg, &g_Reg->AI_CONTROL_REG, "AI_CONTROL_REG"); - AndConstToVariable(1, &g_Reg->AI_CONTROL_REG, "AI_CONTROL_REG"); - case 0x0450000C: - // Clear interrupt - AndConstToVariable((uint32_t)~MI_INTR_AI, &g_Reg->MI_INTR_REG, "MI_INTR_REG"); - AndConstToVariable((uint32_t)~MI_INTR_AI, &g_Reg->m_AudioIntrReg, "m_AudioIntrReg"); - m_RegWorkingSet.BeforeCallDirect(); -#ifdef _MSC_VER - MoveConstToX86reg((uint32_t)g_Reg, x86_ECX); - Call_Direct(AddressOf(&CRegisters::CheckInterrupts), "CRegisters::CheckInterrupts"); -#else - PushImm32((uint32_t)g_Reg); - Call_Direct(AddressOf(&CRegisters::CheckInterrupts), "CRegisters::CheckInterrupts"); - AddConstToX86Reg(x86_ESP, 4); -#endif - m_RegWorkingSet.AfterCallDirect(); - break; - case 0x04500010: - sprintf(VarName, "RDRAM + %X", PAddr); - MoveX86regToVariable(Reg, PAddr + g_MMU->Rdram(), VarName); - break; - case 0x04500014: MoveX86regToVariable(Reg, &g_Reg->AI_BITRATE_REG, "AI_BITRATE_REG"); break; - default: - sprintf(VarName, "RDRAM + %X", PAddr); - MoveX86regToVariable(Reg, PAddr + g_MMU->Rdram(), VarName); - if (ShowUnhandledMemory()) - { - g_Notify->DisplayError(stdstr_f("%s\nTrying to store in %08X?", __FUNCTION__, VAddr).c_str()); - } - } + m_RegWorkingSet.AfterCallDirect(); break; case 0x04600000: switch (PAddr) diff --git a/Source/Project64-core/N64System/SystemGlobals.cpp b/Source/Project64-core/N64System/SystemGlobals.cpp index 440ada03e..8bc897954 100644 --- a/Source/Project64-core/N64System/SystemGlobals.cpp +++ b/Source/Project64-core/N64System/SystemGlobals.cpp @@ -13,7 +13,6 @@ CPlugins * g_Plugins = nullptr; CN64Rom * g_Rom = nullptr; // The current ROM that this system is executing, it can only execute one file at the time CN64Rom * g_DDRom = nullptr; // 64DD IPL ROM CN64Disk * g_Disk = nullptr; // 64DD disk -CAudio * g_Audio = nullptr; CSystemTimer * g_SystemTimer = nullptr; CTransVaddr * g_TransVaddr = nullptr; CSystemEvents * g_SystemEvents = nullptr; diff --git a/Source/Project64-core/N64System/SystemGlobals.h b/Source/Project64-core/N64System/SystemGlobals.h index c4e4fc0b7..f5d5b21d5 100644 --- a/Source/Project64-core/N64System/SystemGlobals.h +++ b/Source/Project64-core/N64System/SystemGlobals.h @@ -30,9 +30,6 @@ extern CN64Rom * g_DDRom; // 64DD IPL ROM class CN64Disk; extern CN64Disk * g_Disk; // 64DD disk -class CAudio; -extern CAudio * g_Audio; - class CSystemTimer; extern CSystemTimer * g_SystemTimer; diff --git a/Source/Project64-core/Project64-core.vcxproj b/Source/Project64-core/Project64-core.vcxproj index c653dba68..e0ceccabf 100644 --- a/Source/Project64-core/Project64-core.vcxproj +++ b/Source/Project64-core/Project64-core.vcxproj @@ -53,6 +53,7 @@ + @@ -155,6 +156,7 @@ + diff --git a/Source/Project64-core/Project64-core.vcxproj.filters b/Source/Project64-core/Project64-core.vcxproj.filters index da8c4bb98..e4d9dec1f 100644 --- a/Source/Project64-core/Project64-core.vcxproj.filters +++ b/Source/Project64-core/Project64-core.vcxproj.filters @@ -378,6 +378,9 @@ Source Files\N64 System\MemoryHandler + + Source Files\N64 System\MemoryHandler + @@ -722,6 +725,9 @@ Header Files\N64 System\MemoryHandler + + Header Files\N64 System\MemoryHandler + diff --git a/Source/Project64/UserInterface/Debugger/DebugMMU.cpp b/Source/Project64/UserInterface/Debugger/DebugMMU.cpp index e3fbe2111..cc70ce295 100644 --- a/Source/Project64/UserInterface/Debugger/DebugMMU.cpp +++ b/Source/Project64/UserInterface/Debugger/DebugMMU.cpp @@ -208,7 +208,7 @@ bool CDebugMMU::GetPhysicalByte(uint32_t paddr, uint8_t* value) if (g_System->bFixedAudio()) { - audioLength = g_Audio->GetLength(); + audioLength = g_MMU->AudioInterface().GetLength(); } else { @@ -222,7 +222,7 @@ bool CDebugMMU::GetPhysicalByte(uint32_t paddr, uint8_t* value) if (paddr >= 0x0450000C && paddr <= 0x0450000F) { - uint32_t audioStatus = g_System->bFixedAudio() ? g_Audio->GetStatus() : g_Reg->AI_STATUS_REG; + uint32_t audioStatus = g_System->bFixedAudio() ? g_MMU->AudioInterface().GetStatus() : g_Reg->AI_STATUS_REG; *value = (audioStatus >> (24 - nByte * 8)) & 0xFF; return true; }