2007-12-01 04:08:34 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
2008-03-29 16:38:35 +00:00
|
|
|
#include <boost/lexical_cast.hpp>
|
2007-12-01 04:08:34 +00:00
|
|
|
#include "Dmac_Channel.h"
|
|
|
|
#include "DMAC.h"
|
2008-03-29 16:38:35 +00:00
|
|
|
#include "RegisterStateFile.h"
|
2012-06-24 19:22:00 +00:00
|
|
|
#include "Log.h"
|
2008-03-29 16:38:35 +00:00
|
|
|
|
2012-06-24 19:22:00 +00:00
|
|
|
#define LOG_NAME ("dmac")
|
2012-05-29 03:10:09 +00:00
|
|
|
#define STATE_PREFIX ("dmac/channel_")
|
|
|
|
#define STATE_SUFFIX (".xml")
|
|
|
|
#define STATE_REGS_CHCR ("CHCR")
|
|
|
|
#define STATE_REGS_MADR ("MADR")
|
|
|
|
#define STATE_REGS_QWC ("QWC")
|
|
|
|
#define STATE_REGS_TADR ("TADR")
|
|
|
|
#define STATE_REGS_SCCTRL ("SCCTRL")
|
|
|
|
#define STATE_REGS_ASR0 ("ASR0")
|
|
|
|
#define STATE_REGS_ASR1 ("ASR1")
|
2007-12-01 04:08:34 +00:00
|
|
|
|
|
|
|
using namespace Dmac;
|
|
|
|
|
2011-06-26 18:54:39 +00:00
|
|
|
CChannel::CChannel(CDMAC& dmac, unsigned int nNumber, const DmaReceiveHandler& pReceive)
|
|
|
|
: m_dmac(dmac)
|
|
|
|
, m_nNumber(nNumber)
|
|
|
|
, m_pReceive(pReceive)
|
2007-12-01 04:08:34 +00:00
|
|
|
{
|
2008-03-29 16:38:35 +00:00
|
|
|
|
2007-12-01 04:08:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CChannel::~CChannel()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void CChannel::Reset()
|
|
|
|
{
|
|
|
|
memset(&m_CHCR, 0, sizeof(CHCR));
|
|
|
|
m_nMADR = 0;
|
|
|
|
m_nQWC = 0;
|
|
|
|
m_nTADR = 0;
|
|
|
|
m_nSCCTRL = 0;
|
2014-04-21 04:28:01 -04:00
|
|
|
m_nASR[0] = 0;
|
|
|
|
m_nASR[1] = 0;
|
2007-12-01 04:08:34 +00:00
|
|
|
}
|
|
|
|
|
2009-08-10 00:56:50 +00:00
|
|
|
void CChannel::SaveState(Framework::CZipArchiveWriter& archive)
|
2008-03-29 16:38:35 +00:00
|
|
|
{
|
2011-06-26 18:54:39 +00:00
|
|
|
std::string path = STATE_PREFIX + boost::lexical_cast<std::string>(m_nNumber) + STATE_SUFFIX;
|
2012-05-29 03:10:09 +00:00
|
|
|
CRegisterStateFile* registerFile = new CRegisterStateFile(path.c_str());
|
|
|
|
registerFile->SetRegister32(STATE_REGS_CHCR, m_CHCR);
|
|
|
|
registerFile->SetRegister32(STATE_REGS_MADR, m_nMADR);
|
|
|
|
registerFile->SetRegister32(STATE_REGS_QWC, m_nQWC);
|
|
|
|
registerFile->SetRegister32(STATE_REGS_TADR, m_nTADR);
|
|
|
|
registerFile->SetRegister32(STATE_REGS_SCCTRL, m_nSCCTRL);
|
|
|
|
registerFile->SetRegister32(STATE_REGS_ASR0, m_nASR[0]);
|
|
|
|
registerFile->SetRegister32(STATE_REGS_ASR1, m_nASR[1]);
|
|
|
|
archive.InsertFile(registerFile);
|
2008-03-29 16:38:35 +00:00
|
|
|
}
|
|
|
|
|
2009-08-10 00:56:50 +00:00
|
|
|
void CChannel::LoadState(Framework::CZipArchiveReader& archive)
|
2008-03-29 16:38:35 +00:00
|
|
|
{
|
2011-06-26 18:54:39 +00:00
|
|
|
std::string path = STATE_PREFIX + boost::lexical_cast<std::string>(m_nNumber) + STATE_SUFFIX;
|
2012-05-29 03:10:09 +00:00
|
|
|
CRegisterStateFile registerFile(*archive.BeginReadFile(path.c_str()));
|
|
|
|
m_CHCR <<= registerFile.GetRegister32(STATE_REGS_CHCR);
|
|
|
|
m_nMADR = registerFile.GetRegister32(STATE_REGS_MADR);
|
|
|
|
m_nQWC = registerFile.GetRegister32(STATE_REGS_QWC);
|
|
|
|
m_nTADR = registerFile.GetRegister32(STATE_REGS_TADR);
|
|
|
|
m_nSCCTRL = registerFile.GetRegister32(STATE_REGS_SCCTRL);
|
|
|
|
m_nASR[0] = registerFile.GetRegister32(STATE_REGS_ASR0);
|
|
|
|
m_nASR[1] = registerFile.GetRegister32(STATE_REGS_ASR1);
|
2008-03-29 16:38:35 +00:00
|
|
|
}
|
|
|
|
|
2007-12-01 04:08:34 +00:00
|
|
|
uint32 CChannel::ReadCHCR()
|
|
|
|
{
|
|
|
|
return *(uint32*)&m_CHCR;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CChannel::WriteCHCR(uint32 nValue)
|
|
|
|
{
|
2008-02-08 22:07:28 +00:00
|
|
|
bool nSuspend = false;
|
2007-12-01 04:08:34 +00:00
|
|
|
|
|
|
|
//We need to check if the purpose of this write is to suspend the current transfer
|
2012-05-29 03:10:09 +00:00
|
|
|
if(m_dmac.m_D_ENABLE & CDMAC::ENABLE_CPND)
|
2007-12-01 04:08:34 +00:00
|
|
|
{
|
2012-05-29 03:10:09 +00:00
|
|
|
nSuspend = (m_CHCR.nSTR != 0) && ((nValue & CDMAC::CHCR_STR) == 0);
|
2007-12-01 04:08:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(m_CHCR.nSTR == 1)
|
|
|
|
{
|
2012-05-29 03:10:09 +00:00
|
|
|
m_CHCR.nSTR = ((nValue & CDMAC::CHCR_STR) != 0) ? 1 : 0;
|
2007-12-01 04:08:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_CHCR = *(CHCR*)&nValue;
|
|
|
|
}
|
2008-03-29 16:38:35 +00:00
|
|
|
|
2012-05-29 03:10:09 +00:00
|
|
|
if(m_CHCR.nSTR != 0)
|
|
|
|
{
|
2011-06-26 18:54:39 +00:00
|
|
|
if(m_nQWC == 0)
|
|
|
|
{
|
|
|
|
m_nSCCTRL |= SCCTRL_INITXFER;
|
|
|
|
}
|
2014-07-12 23:35:23 -04:00
|
|
|
m_nSCCTRL &= ~SCCTRL_RETTOP;
|
2012-05-29 03:10:09 +00:00
|
|
|
Execute();
|
|
|
|
}
|
2007-12-01 04:08:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CChannel::Execute()
|
|
|
|
{
|
|
|
|
if(m_CHCR.nSTR != 0)
|
|
|
|
{
|
2012-05-29 03:10:09 +00:00
|
|
|
if(m_dmac.m_D_ENABLE)
|
|
|
|
{
|
2015-03-22 23:49:32 -04:00
|
|
|
//TODO: Need to check cases where this is done on channels other than 4
|
|
|
|
assert(m_nNumber == 4);
|
2012-05-29 03:10:09 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-06-24 19:22:00 +00:00
|
|
|
if((m_nNumber == 1) && (m_CHCR.nDIR == 0))
|
|
|
|
{
|
|
|
|
//Humm, destination mode, not supported for now.
|
|
|
|
CLog::GetInstance().Print(LOG_NAME, "Warning: Using destination mode for channel %d. Cancelling transfer.\r\n", m_nNumber);
|
2015-03-22 02:57:02 -04:00
|
|
|
ClearSTR();
|
2012-06-24 19:22:00 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-05-29 03:10:09 +00:00
|
|
|
switch(m_CHCR.nMOD)
|
2007-12-01 04:08:34 +00:00
|
|
|
{
|
|
|
|
case 0x00:
|
|
|
|
ExecuteNormal();
|
|
|
|
break;
|
|
|
|
case 0x01:
|
|
|
|
ExecuteSourceChain();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CChannel::ExecuteNormal()
|
|
|
|
{
|
2008-06-23 01:36:43 +00:00
|
|
|
uint32 nRecv = m_pReceive(m_nMADR, m_nQWC, 0, false);
|
2007-12-01 04:08:34 +00:00
|
|
|
|
|
|
|
m_nMADR += nRecv * 0x10;
|
|
|
|
m_nQWC -= nRecv;
|
|
|
|
|
|
|
|
if(m_nQWC == 0)
|
|
|
|
{
|
|
|
|
ClearSTR();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CChannel::ExecuteSourceChain()
|
|
|
|
{
|
|
|
|
//Execute current
|
|
|
|
if(m_nQWC != 0)
|
|
|
|
{
|
2011-06-26 18:54:39 +00:00
|
|
|
uint32 nRecv = m_pReceive(m_nMADR, m_nQWC, 0, false);
|
2007-12-01 04:08:34 +00:00
|
|
|
|
|
|
|
m_nMADR += nRecv * 0x10;
|
|
|
|
m_nQWC -= nRecv;
|
|
|
|
|
|
|
|
if(m_nQWC != 0)
|
|
|
|
{
|
|
|
|
//Transfer isn't finished, suspend for now
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while(m_CHCR.nSTR == 1)
|
|
|
|
{
|
2012-05-29 03:10:09 +00:00
|
|
|
//Check if MFIFO is enabled with this channel
|
|
|
|
if(m_dmac.m_D_CTRL.mfd == 0x02 && m_nNumber == 1)
|
|
|
|
{
|
|
|
|
//Hold transfer if not ready
|
|
|
|
if(m_nTADR == m_dmac.m_D8.m_nMADR)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// uint32 currentPos = (m_nTADR & m_dmac.m_D_RBSR) + m_dmac.m_D_RBOR;
|
|
|
|
// if(currentPos != m_nTADR)
|
|
|
|
// {
|
|
|
|
// int i = 0;
|
|
|
|
// i++;
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
//Check if device received DMAtag
|
|
|
|
if(m_CHCR.nReserved0)
|
|
|
|
{
|
|
|
|
assert(m_CHCR.nTTE);
|
|
|
|
m_CHCR.nReserved0 = 0;
|
|
|
|
if(m_pReceive(m_nTADR, 1, 0, true) != 1)
|
|
|
|
{
|
|
|
|
//Device didn't receive DmaTag, break for now
|
|
|
|
m_CHCR.nReserved0 = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Check if we've finished our DMA transfer
|
2011-06-26 18:54:39 +00:00
|
|
|
if(m_nQWC == 0)
|
2012-05-29 03:10:09 +00:00
|
|
|
{
|
|
|
|
if(m_nSCCTRL & SCCTRL_INITXFER)
|
|
|
|
{
|
|
|
|
//Clear this bit
|
|
|
|
m_nSCCTRL &= ~SCCTRL_INITXFER;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(CDMAC::IsEndTagId((uint32)m_CHCR.nTAG << 16))
|
|
|
|
{
|
|
|
|
ClearSTR();
|
|
|
|
continue;
|
|
|
|
}
|
2014-07-12 23:35:23 -04:00
|
|
|
|
|
|
|
//Transfer must also end when we encounter a RET tag with ASR == 0
|
|
|
|
if(m_nSCCTRL & SCCTRL_RETTOP)
|
|
|
|
{
|
|
|
|
ClearSTR();
|
|
|
|
m_nSCCTRL &= ~SCCTRL_RETTOP;
|
|
|
|
continue;
|
|
|
|
}
|
2012-05-29 03:10:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Suspend transfer
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_CHCR.nTTE == 1)
|
|
|
|
{
|
|
|
|
m_CHCR.nReserved0 = 0;
|
|
|
|
if(m_pReceive(m_nTADR, 1, 0, true) != 1)
|
|
|
|
{
|
|
|
|
//Device didn't receive DmaTag, break for now
|
|
|
|
m_CHCR.nReserved0 = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Half-Life does this...
|
|
|
|
if(m_nTADR == 0)
|
|
|
|
{
|
|
|
|
ClearSTR();
|
|
|
|
continue;
|
|
|
|
}
|
2009-05-30 15:26:13 +00:00
|
|
|
|
2011-06-26 18:54:39 +00:00
|
|
|
uint64 nTag = m_dmac.FetchDMATag(m_nTADR);
|
2007-12-01 04:08:34 +00:00
|
|
|
|
|
|
|
//Save higher 16 bits of tag into CHCR
|
|
|
|
m_CHCR.nTAG = nTag >> 16;
|
|
|
|
//m_nCHCR &= ~0xFFFF0000;
|
|
|
|
//m_nCHCR |= nTag & 0xFFFF0000;
|
|
|
|
|
2015-04-03 00:35:49 -04:00
|
|
|
uint8 nID = static_cast<uint8>((nTag >> 28) & 0x07);
|
2007-12-01 04:08:34 +00:00
|
|
|
|
|
|
|
switch(nID)
|
|
|
|
{
|
2015-04-03 00:35:49 -04:00
|
|
|
case DMATAG_REFE:
|
2007-12-01 04:08:34 +00:00
|
|
|
//REFE - Data to transfer is pointer in memory address, transfer is done
|
|
|
|
m_nMADR = (uint32)((nTag >> 32) & 0xFFFFFFFF);
|
|
|
|
m_nQWC = (uint32)((nTag >> 0) & 0x0000FFFF);
|
|
|
|
m_nTADR = m_nTADR + 0x10;
|
|
|
|
break;
|
2015-04-03 00:35:49 -04:00
|
|
|
case DMATAG_CNT:
|
2007-12-01 04:08:34 +00:00
|
|
|
//CNT - Data to transfer is after the tag, next tag is after the data
|
|
|
|
m_nMADR = m_nTADR + 0x10;
|
|
|
|
m_nQWC = (uint32)(nTag & 0xFFFF);
|
|
|
|
m_nTADR = (m_nQWC * 0x10) + m_nMADR;
|
|
|
|
break;
|
2015-04-03 00:35:49 -04:00
|
|
|
case DMATAG_NEXT:
|
2007-12-01 04:08:34 +00:00
|
|
|
//NEXT - Transfers data after tag, next tag is at position in ADDR field
|
|
|
|
m_nMADR = m_nTADR + 0x10;
|
|
|
|
m_nQWC = (uint32)((nTag >> 0) & 0x0000FFFF);
|
|
|
|
m_nTADR = (uint32)((nTag >> 32) & 0xFFFFFFFF);
|
|
|
|
break;
|
2015-04-03 00:35:49 -04:00
|
|
|
case DMATAG_REF:
|
|
|
|
case DMATAG_REFS:
|
2012-05-29 03:10:09 +00:00
|
|
|
//REF/REFS - Data to transfer is pointed in memory address, next tag is after this tag
|
2007-12-01 04:08:34 +00:00
|
|
|
m_nMADR = (uint32)((nTag >> 32) & 0xFFFFFFFF);
|
|
|
|
m_nQWC = (uint32)((nTag >> 0) & 0x0000FFFF);
|
|
|
|
m_nTADR = m_nTADR + 0x10;
|
|
|
|
break;
|
2015-04-03 00:35:49 -04:00
|
|
|
case DMATAG_CALL:
|
2007-12-01 04:08:34 +00:00
|
|
|
//CALL - Transfers QWC after the tag, saves next address in ASR, TADR = ADDR
|
|
|
|
assert(m_CHCR.nASP < 2);
|
|
|
|
m_nMADR = m_nTADR + 0x10;
|
|
|
|
m_nQWC = (uint32)(nTag & 0xFFFF);
|
|
|
|
m_nASR[m_CHCR.nASP] = m_nMADR + (m_nQWC * 0x10);
|
|
|
|
m_nTADR = (uint32)((nTag >> 32) & 0xFFFFFFFF);
|
|
|
|
m_CHCR.nASP++;
|
|
|
|
break;
|
2015-04-03 00:35:49 -04:00
|
|
|
case DMATAG_RET:
|
2007-12-01 04:08:34 +00:00
|
|
|
//RET - Transfers QWC after the tag, pops TADR from ASR
|
2014-07-11 22:29:42 +01:00
|
|
|
m_nMADR = m_nTADR + 0x10;
|
|
|
|
m_nQWC = (uint32)(nTag & 0xFFFF);
|
2014-07-12 23:35:23 -04:00
|
|
|
if(m_CHCR.nASP > 0)
|
|
|
|
{
|
2014-07-11 22:29:42 +01:00
|
|
|
m_CHCR.nASP--;
|
|
|
|
m_nTADR = m_nASR[m_CHCR.nASP];
|
|
|
|
}
|
2014-07-12 23:35:23 -04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
m_nSCCTRL |= SCCTRL_RETTOP;
|
2014-07-11 22:29:42 +01:00
|
|
|
}
|
2007-12-01 04:08:34 +00:00
|
|
|
break;
|
2015-04-03 00:35:49 -04:00
|
|
|
case DMATAG_END:
|
2007-12-01 04:08:34 +00:00
|
|
|
//END - Data to transfer is after the tag, transfer is finished
|
|
|
|
m_nMADR = m_nTADR + 0x10;
|
|
|
|
m_nQWC = (uint32)(nTag & 0xFFFF);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
m_nQWC = 0;
|
|
|
|
assert(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_nQWC != 0)
|
|
|
|
{
|
2011-06-26 18:54:39 +00:00
|
|
|
uint32 nRecv = m_pReceive(m_nMADR, m_nQWC, 0, false);
|
2007-12-01 04:08:34 +00:00
|
|
|
|
|
|
|
m_nMADR += nRecv * 0x10;
|
|
|
|
m_nQWC -= nRecv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CChannel::SetReceiveHandler(const DmaReceiveHandler& handler)
|
|
|
|
{
|
2012-05-29 03:10:09 +00:00
|
|
|
m_pReceive = handler;
|
2007-12-01 04:08:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CChannel::ClearSTR()
|
|
|
|
{
|
|
|
|
m_CHCR.nSTR = ~m_CHCR.nSTR;
|
|
|
|
|
|
|
|
//Set interrupt
|
|
|
|
m_dmac.m_D_STAT |= (1 << m_nNumber);
|
2009-05-30 15:26:13 +00:00
|
|
|
|
|
|
|
m_dmac.UpdateCpCond();
|
2007-12-01 04:08:34 +00:00
|
|
|
}
|