Play-/Source/iop/Iop_DmacChannel.cpp
Björn Gerdau edcfb4ea1f
Add dedicated intr line parameter for DMAC channels
DMAC channel IDs don't necessarily match the interrupt line which is asserted on DMA finish.
2021-06-07 14:34:16 +02:00

115 lines
2.7 KiB
C++

#include <assert.h>
#include "string_format.h"
#include "Iop_DmacChannel.h"
#include "Iop_Dmac.h"
#include "Iop_Intc.h"
#include "../states/RegisterStateFile.h"
using namespace Iop;
using namespace Iop::Dmac;
#define STATE_REGS_XML_FORMAT ("iop_dmac/channel_%d.xml")
#define STATE_REGS_CHCR ("CHCR")
#define STATE_REGS_BCR ("BCR")
#define STATE_REGS_MADR ("MADR")
CChannel::CChannel(uint32 baseAddress, unsigned int number, unsigned int intrLine, CDmac& dmac)
: m_dmac(dmac)
, m_number(number)
, m_intrLine(intrLine)
, m_baseAddress(baseAddress)
{
Reset();
}
void CChannel::Reset()
{
m_CHCR <<= 0;
m_BCR <<= 0;
m_MADR = 0;
}
void CChannel::LoadState(Framework::CZipArchiveReader& archive)
{
auto path = string_format(STATE_REGS_XML_FORMAT, m_number);
auto registerFile = CRegisterStateFile(*archive.BeginReadFile(path.c_str()));
m_CHCR <<= registerFile.GetRegister32(STATE_REGS_CHCR);
m_BCR <<= registerFile.GetRegister32(STATE_REGS_BCR);
m_MADR = registerFile.GetRegister32(STATE_REGS_MADR);
}
void CChannel::SaveState(Framework::CZipArchiveWriter& archive)
{
auto path = string_format(STATE_REGS_XML_FORMAT, m_number);
auto registerFile = new CRegisterStateFile(path.c_str());
registerFile->SetRegister32(STATE_REGS_CHCR, m_CHCR);
registerFile->SetRegister32(STATE_REGS_BCR, m_BCR);
registerFile->SetRegister32(STATE_REGS_MADR, m_MADR);
archive.InsertFile(registerFile);
}
void CChannel::SetReceiveFunction(const ReceiveFunctionType& receiveFunction)
{
m_receiveFunction = receiveFunction;
}
void CChannel::ResumeDma()
{
if(m_CHCR.tr == 0) return;
assert(m_CHCR.co == 1);
assert(m_receiveFunction);
uint32 address = m_MADR & 0x1FFFFFFF;
uint32 blocksTransfered = m_receiveFunction(m_dmac.GetRam() + address, m_BCR.bs * 4, m_BCR.ba, m_CHCR.dr);
assert(blocksTransfered <= m_BCR.ba);
m_BCR.ba -= blocksTransfered;
m_MADR += (m_BCR.bs * 4) * blocksTransfered;
if(m_BCR.ba == 0)
{
//Trigger interrupt
m_CHCR.tr = 0;
m_dmac.AssertLine(m_intrLine - CIntc::LINE_DMA_BASE);
}
}
uint32 CChannel::ReadRegister(uint32 address)
{
switch(address - m_baseAddress)
{
case REG_MADR:
return m_MADR;
break;
case REG_BCR:
return m_BCR;
break;
case REG_CHCR:
return m_CHCR;
break;
}
return 0;
}
void CChannel::WriteRegister(uint32 address, uint32 value)
{
assert(m_CHCR.tr == 0);
switch(address - m_baseAddress)
{
case REG_MADR:
m_MADR = value;
break;
case REG_BCR:
m_BCR <<= value;
break;
case REG_BCR + 2:
//Not really cool...
m_BCR.ba = static_cast<uint16>(value);
break;
case REG_CHCR:
m_CHCR <<= value;
if(m_CHCR.tr)
{
ResumeDma();
}
break;
}
}