Synchronous DMAC's now working properly, should be better for the SPU2 situation. Any peripherals that send data will probably need to wait for the DMAC to receive it as well, not up to that yet.

This commit is contained in:
Marco Satti 2017-06-28 21:44:00 +08:00
parent 9ac4ebcc9f
commit 667aea2f93
8 changed files with 40 additions and 62 deletions

View file

@ -43,10 +43,10 @@ Parameters still need to be set upon constructing the object.
Define if the emulator should log EE and IOP DMAC transfer and/or tag activity (source/chain/list modes).
*/
#if defined(BUILD_DEBUG)
#define DEBUG_LOG_EE_DMAC_XFERS 1
#define DEBUG_LOG_IOP_DMAC_XFERS 1
#define DEBUG_LOG_EE_DMAC_TAGS 1
#define DEBUG_LOG_IOP_DMAC_TAGS 1
#define DEBUG_LOG_EE_DMAC_XFERS 0
#define DEBUG_LOG_IOP_DMAC_XFERS 0
#define DEBUG_LOG_EE_DMAC_TAGS 0
#define DEBUG_LOG_IOP_DMAC_TAGS 0
#else
#define DEBUG_LOG_EE_DMA_TAGS 0
#define DEBUG_LOG_IOP_DMA_TAGS 0

View file

@ -58,42 +58,25 @@ IOPDmacChannelRegister_BCR_t::IOPDmacChannelRegister_BCR_t(const char * mnemonic
registerField(Fields::BA, "BA", 16, 16, 0);
}
void IOPDmacChannelRegister_BCR_t::writeHword(const Context_t context, const size_t arrayIndex, const u16 value)
void IOPDmacChannelRegister_BCR_t::calculate(const Context_t context, const bool useBA)
{
BitfieldRegister32_t::writeHword(context, arrayIndex, value);
// Update the transfer size.
// When BS = 0, it is interpreted as 0x10000 (see wisi and sp193's IOP DMAC docs).
// Wben BA = 0, just use 1 in its place (0 implies it is being used in a burst context).
// TODO: I'm actually not sure if when BA = 0, does it mean 0x10000 (same as BS). Wouldn't think so...
u16 BS = static_cast<u16>(getFieldValue(context, Fields::BS));
u16 BA = static_cast<u16>(getFieldValue(context, Fields::BA));
BA = (BA > 0) ? BA : 1;
if (BS == 0)
mTransferLength = BA * 0x10000;
else
// Wben BA = 0, it is also interpreted as 0x10000 (see wisi and sp193's IOP DMAC docs).
if (useBA)
{
u32 BS = getFieldValue(context, Fields::BS);
u32 BA = getFieldValue(context, Fields::BA);
BS = (BS > 0) ? BS : 0x10000;
BA = (BA > 0) ? BA : 0x10000;
mTransferLength = BA * BS;
if (mTransferLength == 0)
log(Debug, "IOP DMA BCR register's transfer size set is 0 - please check!");
}
void IOPDmacChannelRegister_BCR_t::writeWord(const Context_t context, const u32 value)
{
BitfieldRegister32_t::writeWord(context, value);
// Update the transfer size.
// When BS = 0, it is interpreted as 0x10000 (see wisi and sp193's IOP DMAC docs).
u16 BS = static_cast<u16>(getFieldValue(context, Fields::BS));
u16 BA = static_cast<u16>(getFieldValue(context, Fields::BA));
if (BS == 0)
mTransferLength = BA * 0x10000;
}
else
mTransferLength = BA * BS;
if (mTransferLength == 0)
log(Debug, "IOP DMA BCR register's transfer size set is 0 - please check!");
{
u32 BS = getFieldValue(context, Fields::BS);
BS = (BS > 0) ? BS : 0x10000;
mTransferLength = BS;
}
}
IOPDmacChannelRegister_MADR_t::IOPDmacChannelRegister_MADR_t(const char * mnemonic, const bool debugReads, const bool debugWrites) :

View file

@ -82,15 +82,16 @@ public:
IOPDmacChannelRegister_BCR_t(const char * mnemonic, const bool debugReads, const bool debugWrites);
/*
Upon writing, stores the transfer size to be used by the DMAC.
Calculates the transfer size, based on the BCR register value.
Call this before starting a DMA transfer, and then use the transfer length member directly.
Parsing useBA = true means a block mode transfer size is calculated, otherwise, slice mode is calculated.
*/
void writeHword(const Context_t context, const size_t arrayIndex, const u16 value) override;
void writeWord(const Context_t context, const u32 value) override;
void calculate(const Context_t context, const bool useBA);
/*
Transfer size.
The register value is not meant to change during the transfer.
Instead, we calculate the total length and use result here.
Instead, the total length after calling calculate() is stored here.
This is directly accessible to the IOP DMAC which manipulates this value.
*/
size_t mTransferLength;

View file

@ -101,8 +101,8 @@ IOPDmacChannel_SIF0_t::IOPDmacChannel_SIF0_t(const std::shared_ptr<FIFOQueue_t>
IOPDmacChannel_t(CHANNEL_ID, fifoQueue)
{
MADR = std::make_shared<IOPDmacChannelRegister_MADR_t>("IOP DMAC CH_SIF0 MADR", false, false);
BCR = std::make_shared<IOPDmacChannelRegister_BCR_t>("IOP DMAC CH_SIF0 BCR", false, true);
CHCR = std::make_shared<IOPDmacChannelRegister_SIF0_CHCR_t>("IOP DMAC CH_SIF0 CHCR", false, true, sbusF240);
BCR = std::make_shared<IOPDmacChannelRegister_BCR_t>("IOP DMAC CH_SIF0 BCR", false, false);
CHCR = std::make_shared<IOPDmacChannelRegister_SIF0_CHCR_t>("IOP DMAC CH_SIF0 CHCR", false, false, sbusF240);
TADR = std::make_shared<IOPDmacChannelRegister_TADR_t>("IOP DMAC CH_SIF0 TADR", false, false);
}
@ -110,8 +110,8 @@ IOPDmacChannel_SIF1_t::IOPDmacChannel_SIF1_t(const std::shared_ptr<FIFOQueue_t>
IOPDmacChannel_t(CHANNEL_ID, fifoQueue)
{
MADR = std::make_shared<IOPDmacChannelRegister_MADR_t>("IOP DMAC CH_SIF1 MADR", false, false);
BCR = std::make_shared<IOPDmacChannelRegister_BCR_t>("IOP DMAC CH_SIF1 BCR", false, true);
CHCR = std::make_shared<IOPDmacChannelRegister_SIF1_CHCR_t>("IOP DMAC CH_SIF1 CHCR", false, true, sbusF240);
BCR = std::make_shared<IOPDmacChannelRegister_BCR_t>("IOP DMAC CH_SIF1 BCR", false, false);
CHCR = std::make_shared<IOPDmacChannelRegister_SIF1_CHCR_t>("IOP DMAC CH_SIF1 CHCR", false, false, sbusF240);
}
IOPDmacChannel_fromSIO2_t::IOPDmacChannel_fromSIO2_t(const std::shared_ptr<FIFOQueue_t> & fifoQueue) :

View file

@ -223,7 +223,7 @@ SPU2Core_C0_t::SPU2Core_C0_t(const std::shared_ptr<IOPDmacChannel_t> & dmaChanne
EEAL = std::make_shared<Register16_t>("SPU2 C0 EEAL", false, false);
ENDX0 = std::make_shared<Register16_t>("SPU2 C0 ENDX0", false, false);
ENDX1 = std::make_shared<Register16_t>("SPU2 C0 ENDX1", false, false);
STATX = std::make_shared<SPU2CoreRegister_STATX_t>("SPU2 C0 STATX", true, false);
STATX = std::make_shared<SPU2CoreRegister_STATX_t>("SPU2 C0 STATX", false, false);
MVOLL = std::make_shared<SPU2CoreRegister_VOL_t>("SPU2 C0 MVOLL", false, false);
MVOLR = std::make_shared<SPU2CoreRegister_VOL_t>("SPU2 C0 MVOLR", false, false);
EVOLL = std::make_shared<Register16_t>("SPU2 C0 EVOLL", false, false);
@ -372,7 +372,7 @@ SPU2Core_C1_t::SPU2Core_C1_t(const std::shared_ptr<IOPDmacChannel_t> & dmaChanne
EEAL = std::make_shared<Register16_t>("SPU2 C1 EEAL", false, false);
ENDX0 = std::make_shared<Register16_t>("SPU2 C1 ENDX0", false, false);
ENDX1 = std::make_shared<Register16_t>("SPU2 C1 ENDX1", false, false);
STATX = std::make_shared<SPU2CoreRegister_STATX_t>("SPU2 C1 STATX", true, false);
STATX = std::make_shared<SPU2CoreRegister_STATX_t>("SPU2 C1 STATX", false, false);
MVOLL = std::make_shared<SPU2CoreRegister_VOL_t>("SPU2 C1 MVOLL", false, false);
MVOLR = std::make_shared<SPU2CoreRegister_VOL_t>("SPU2 C1 MVOLR", false, false);
EVOLL = std::make_shared<Register16_t>("SPU2 C1 EVOLL", false, false);

View file

@ -209,8 +209,6 @@ int EEDmac_s::transferData() const
void EEDmac_s::setStateSuspended() const
{
log(Debug, "EE DMAC channel %s (direction = %s) interrupt.", mChannel->getInfo()->mMnemonic, mChannel->CHCR->getDirection(getContext()) == Direction_t::TO ? "To" : "From");
// Emit the interrupt status bit.
mDMAC->STAT->setFieldValue(getContext(), EEDmacRegister_STAT_t::Fields::CHANNEL_CIS_KEYS[mChannel->getChannelID()], 1);

View file

@ -120,11 +120,8 @@ bool IOPDmac_s::transferNormalBurst()
// Perform pre-start checks.
if (!mChannel->CHCR->mDMAStarted)
{
// Check the BCR register, make sure that size > 0 in order to start transfer.
if (mChannel->BCR->mTransferLength == 0)
{
throw std::runtime_error("IOP DMAC tried to start transfer without any length - panic!");
}
// Calculate transfer size.
mChannel->BCR->calculate(getContext(), false);
// Pre checks ok - start the DMA transfer.
mChannel->CHCR->mDMAStarted = true;
@ -163,11 +160,8 @@ bool IOPDmac_s::transferNormalSlice()
// Perform pre-start checks.
if (!mChannel->CHCR->mDMAStarted)
{
// Check the BCR register, make sure that size > 0 in order to start transfer.
if (mChannel->BCR->mTransferLength == 0)
{
throw std::runtime_error("IOP DMAC tried to start transfer without any length - panic!");
}
// Calculate transfer size.
mChannel->BCR->calculate(getContext(), true);
// Pre checks ok - start the DMA transfer.
mChannel->CHCR->mDMAStarted = true;
@ -206,8 +200,12 @@ bool IOPDmac_s::transferChain()
// Perform pre-start checks.
if (!mChannel->CHCR->mDMAStarted)
{
// No prechecks needed - start DMA transfer.
// If BCR transfer size is 0 initially, then it just means that we read a tag straight away.
// Tag length is always 0 to begin with.
// TODO: wisi's DMA docs mention the TBCR register, which is not implemented, but used instead over BCR for chain modes (tag transfer length).
// But for the IOP, TBCR is always 0 so far. If emulator crashes with MMU error, this will probably need to be put in.
mChannel->BCR->mTransferLength = 0;
// Pre checks ok - start the DMA transfer.
mChannel->CHCR->mDMAStarted = true;
return true;
}
@ -347,8 +345,6 @@ int IOPDmac_s::transferData() const
void IOPDmac_s::setStateSuspended() const
{
log(Debug, "IOP DMAC channel %s (direction = %s) interrupt.", mChannel->getInfo()->mMnemonic, mChannel->CHCR->getDirection(getContext()) == Direction_t::TO ? "To" : "From");
// Stop channel.
mChannel->CHCR->setFieldValue(getContext(), IOPDmacChannelRegister_CHCR_t::Fields::Start, 0);

View file

@ -188,7 +188,7 @@ int SPU2_s::transferData_ADMA_Write() const
// Only need to control ADMA if channel has been stopped from the IOP side - ie: it will not be sending any more data.
if (mCore->DMAChannel->CHCR->getFieldValue(getContext(), IOPDmacChannelRegister_CHCR_t::Fields::Start) == 0)
{
// If there is no more data in the FIFO queue, and the IOP channel is not on, then send a DMA interrupt.
// If there is no more data in the FIFO queue, then stop.
if (mCore->FIFOQueue->getReadAvailable() == 0)
{
// Set STATX to signal we need data.