diff --git a/Source/ee/DMAC.cpp b/Source/ee/DMAC.cpp index a315ce44..0e8cad41 100644 --- a/Source/ee/DMAC.cpp +++ b/Source/ee/DMAC.cpp @@ -12,6 +12,7 @@ #define STATE_REGS_CTRL ("D_CTRL") #define STATE_REGS_STAT ("D_STAT") #define STATE_REGS_PCR ("D_PCR") +#define STATE_REGS_SQWC ("D_SQWC") #define STATE_REGS_RBSR ("D_RBSR") #define STATE_REGS_RBOR ("D_RBOR") #define STATE_REGS_D8_SADR ("D8_SADR") @@ -79,6 +80,7 @@ void CDMAC::Reset() m_D_STAT = 0; m_D_ENABLE = 0; m_D_PCR = 0; + m_D_SQWC <<= 0; m_D_RBSR = 0; m_D_RBOR = 0; @@ -412,6 +414,10 @@ uint32 CDMAC::GetRegister(uint32 nAddress) return m_D_PCR; break; + case D_SQWC: + return m_D_SQWC; + break; + case D_ENABLER + 0x0: return m_D_ENABLE; break; @@ -788,6 +794,14 @@ void CDMAC::SetRegister(uint32 nAddress, uint32 nData) case D_PCR + 0xC: break; + case D_SQWC + 0x0: + m_D_SQWC <<= nData; + break; + case D_SQWC + 0x4: + case D_SQWC + 0x8: + case D_SQWC + 0xC: + break; + case D_RBSR + 0x0: m_D_RBSR = nData; assert((m_D_RBSR & 0xF) == 0); @@ -831,6 +845,7 @@ void CDMAC::LoadState(Framework::CZipArchiveReader& archive) m_D_CTRL <<= registerFile.GetRegister32(STATE_REGS_CTRL); m_D_STAT = registerFile.GetRegister32(STATE_REGS_STAT); m_D_PCR = registerFile.GetRegister32(STATE_REGS_PCR); + m_D_SQWC <<= registerFile.GetRegister32(STATE_REGS_SQWC); m_D_RBSR = registerFile.GetRegister32(STATE_REGS_RBSR); m_D_RBOR = registerFile.GetRegister32(STATE_REGS_RBOR); m_D8_SADR = registerFile.GetRegister32(STATE_REGS_D8_SADR); @@ -850,6 +865,7 @@ void CDMAC::SaveState(Framework::CZipArchiveWriter& archive) registerFile->SetRegister32(STATE_REGS_CTRL, m_D_CTRL); registerFile->SetRegister32(STATE_REGS_STAT, m_D_STAT); registerFile->SetRegister32(STATE_REGS_PCR, m_D_PCR); + registerFile->SetRegister32(STATE_REGS_SQWC, m_D_SQWC); registerFile->SetRegister32(STATE_REGS_RBSR, m_D_RBSR); registerFile->SetRegister32(STATE_REGS_RBOR, m_D_RBOR); registerFile->SetRegister32(STATE_REGS_D8_SADR, m_D8_SADR); @@ -963,6 +979,9 @@ void CDMAC::DisassembleGet(uint32 nAddress) case D_PCR: CLog::GetInstance().Print(LOG_NAME, "= D_PCR.\r\n"); break; + case D_SQWC: + CLog::GetInstance().Print(LOG_NAME, "= D_SQWC.\r\n"); + break; case D_ENABLER: CLog::GetInstance().Print(LOG_NAME, "= D_ENABLER.\r\n"); break; @@ -1078,6 +1097,9 @@ void CDMAC::DisassembleSet(uint32 nAddress, uint32 nData) case D_PCR: CLog::GetInstance().Print(LOG_NAME, "D_PCR = 0x%0.8X.\r\n", nData); break; + case D_SQWC: + CLog::GetInstance().Print(LOG_NAME, "D_SQWC = 0x%0.8X.\r\n", nData); + break; case D_RBSR: CLog::GetInstance().Print(LOG_NAME, "D_RBSR = 0x%0.8X.\r\n", nData); break; diff --git a/Source/ee/DMAC.h b/Source/ee/DMAC.h index 75fd1e0b..64230b00 100644 --- a/Source/ee/DMAC.h +++ b/Source/ee/DMAC.h @@ -75,6 +75,7 @@ public: D_CTRL = 0x1000E000, D_STAT = 0x1000E010, D_PCR = 0x1000E020, + D_SQWC = 0x1000E030, D_RBSR = 0x1000E040, D_RBOR = 0x1000E050, @@ -130,6 +131,15 @@ private: }; static_assert(sizeof(D_CTRL_REG) == sizeof(uint32), "Size of D_CTRL_REG struct must be 4 bytes."); + struct D_SQWC_REG : public convertible + { + unsigned int sqwc : 8; + unsigned int reserved0 : 8; + unsigned int tqwc : 8; + unsigned int reserved1 : 8; + }; + static_assert(sizeof(D_SQWC_REG) == sizeof(uint32), "Size of D_SQWC_REG struct must be 4 bytes."); + uint64 FetchDMATag(uint32); uint32 ReceiveDMA8(uint32, uint32, uint32, bool); @@ -141,6 +151,7 @@ private: uint32 m_D_STAT; uint32 m_D_ENABLE; uint32 m_D_PCR; + D_SQWC_REG m_D_SQWC; uint32 m_D_RBSR; uint32 m_D_RBOR; diff --git a/Source/ee/Dmac_Channel.cpp b/Source/ee/Dmac_Channel.cpp index 8ea266aa..afa0f3b3 100644 --- a/Source/ee/Dmac_Channel.cpp +++ b/Source/ee/Dmac_Channel.cpp @@ -127,6 +127,18 @@ void CChannel::Execute() case 0x00: ExecuteNormal(); break; + case 0x02: + assert((m_nNumber == CDMAC::CHANNEL_ID_FROM_SPR) || (m_nNumber == CDMAC::CHANNEL_ID_TO_SPR)); + if((m_dmac.m_D_SQWC.sqwc == 0) || (m_dmac.m_D_SQWC.tqwc == 0)) + { + //If SQWC or TQWC is 0, execute normally + ExecuteNormal(); + } + else + { + ExecuteInterleave(); + } + break; case 0x01: case 0x03: //FFXII uses 3 here, assuming source chain mode ExecuteSourceChain(); @@ -173,6 +185,32 @@ void CChannel::ExecuteNormal() } } +void CChannel::ExecuteInterleave() +{ + assert((m_nQWC % m_dmac.m_D_SQWC.tqwc) == 0); + while(1) + { + //Transfer + { + uint32 qwc = m_dmac.m_D_SQWC.tqwc; + uint32 recv = m_pReceive(m_nMADR, qwc, 0, false); + assert(recv == qwc); + + m_nMADR += recv * 0x10; + m_nQWC -= recv; + } + + //Skip + m_nMADR += m_dmac.m_D_SQWC.sqwc * 0x10; + + if(m_nQWC == 0) + { + ClearSTR(); + break; + } + } +} + void CChannel::ExecuteSourceChain() { //Execute current diff --git a/Source/ee/Dmac_Channel.h b/Source/ee/Dmac_Channel.h index 65e3a24e..86f089d7 100644 --- a/Source/ee/Dmac_Channel.h +++ b/Source/ee/Dmac_Channel.h @@ -52,6 +52,7 @@ namespace Dmac void WriteCHCR(uint32); void Execute(); void ExecuteNormal(); + void ExecuteInterleave(); void ExecuteSourceChain(); void SetReceiveHandler(const DmaReceiveHandler&);