Added support for interleave DMA transfer mode.

This commit is contained in:
Jean-Philip Desjardins 2015-10-25 21:32:36 -04:00
parent c38bfc2e21
commit eb66e94752
4 changed files with 72 additions and 0 deletions

View File

@ -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;

View File

@ -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<uint32>
{
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;

View File

@ -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

View File

@ -52,6 +52,7 @@ namespace Dmac
void WriteCHCR(uint32);
void Execute();
void ExecuteNormal();
void ExecuteInterleave();
void ExecuteSourceChain();
void SetReceiveHandler(const DmaReceiveHandler&);