Added wrapper registers for the PCR and ICR's of the IOP DMAC, still toying around with the SPU2 (which requires direct access to IOP DMAC registers).

This commit is contained in:
Marco Satti 2017-06-25 00:10:26 +08:00
parent e937f2ec11
commit 2add548fd7
12 changed files with 383 additions and 87 deletions

View file

@ -25,6 +25,8 @@ IOPDmac_t::IOPDmac_t() :
ICR0(std::make_shared<IOPDmacRegister_ICR0_t>("IOP DMAC ICR0", false, false)),
PCR1(std::make_shared<IOPDmacRegister_PCR1_t>("IOP DMAC PCR1", false, false)),
ICR1(std::make_shared<IOPDmacRegister_ICR1_t>("IOP DMAC ICR1", false, false, ICR0)),
PCRW(std::make_shared<IOPDmacRegister_PCRW_t>(PCR0, PCR1)),
ICRW(std::make_shared<IOPDmacRegister_ICRW_t>(ICR0, ICR1)),
GCTRL(std::make_shared<Register32_t>("IOP DMAC GCTRL", false, false))
{
}

View file

@ -25,6 +25,8 @@ class IOPDmacRegister_ICR0_t;
class IOPDmacRegister_PCR1_t;
class IOPDmacRegister_ICR1_t;
class Register32_t;
class IOPDmacRegister_PCRW_t;
class IOPDmacRegister_ICRW_t;
/*
IOP DMAC resources.
@ -57,11 +59,14 @@ public:
/*
DMAC Common Registers.
Wrappers are provided for the PCR and ICR registers, to appear as one.
*/
std::shared_ptr<IOPDmacRegister_PCR0_t> PCR0;
std::shared_ptr<IOPDmacRegister_ICR0_t> ICR0;
std::shared_ptr<IOPDmacRegister_PCR1_t> PCR1;
std::shared_ptr<IOPDmacRegister_ICR1_t> ICR1;
std::shared_ptr<IOPDmacRegister_PCRW_t> PCRW;
std::shared_ptr<IOPDmacRegister_ICRW_t> ICRW;
std::shared_ptr<Register32_t> GCTRL;
};

View file

@ -1,9 +1,12 @@
#include "Resources/IOP/DMAC/Types/IOPDmacRegisters_t.h"
#include "Resources/IOP/DMAC/Types/IOPDmacChannels_t.h"
constexpr int IOPDmacRegister_PCR0_t::Fields::CHANNEL_PRIORITY_KEYS[Constants::IOP::DMAC::NUMBER_DMAC_CHANNELS / 2];
constexpr int IOPDmacRegister_PCR0_t::Fields::CHANNEL_ENABLE_KEYS[Constants::IOP::DMAC::NUMBER_DMAC_CHANNELS / 2];
constexpr int IOPDmacRegister_ICR0_t::Fields::CHANNEL_IRM_KEYS[Constants::IOP::DMAC::NUMBER_DMAC_CHANNELS / 2];
constexpr int IOPDmacRegister_ICR0_t::Fields::CHANNEL_TCM_KEYS[Constants::IOP::DMAC::NUMBER_DMAC_CHANNELS / 2];
constexpr int IOPDmacRegister_ICR0_t::Fields::CHANNEL_TCI_KEYS[Constants::IOP::DMAC::NUMBER_DMAC_CHANNELS / 2];
constexpr int IOPDmacRegister_PCR1_t::Fields::CHANNEL_PRIORITY_KEYS[Constants::IOP::DMAC::NUMBER_DMAC_CHANNELS / 2];
constexpr int IOPDmacRegister_PCR1_t::Fields::CHANNEL_ENABLE_KEYS[Constants::IOP::DMAC::NUMBER_DMAC_CHANNELS / 2];
constexpr int IOPDmacRegister_ICR1_t::Fields::CHANNEL_IQE_KEYS[Constants::IOP::DMAC::NUMBER_DMAC_CHANNELS];
constexpr int IOPDmacRegister_ICR1_t::Fields::CHANNEL_TCM_KEYS[Constants::IOP::DMAC::NUMBER_DMAC_CHANNELS / 2];
@ -164,3 +167,144 @@ bool IOPDmacRegister_ICR1_t::isInterruptPending(const Context_t context)
return false;
}
IOPDmacRegister_PCRW_t::IOPDmacRegister_PCRW_t(const std::shared_ptr<IOPDmacRegister_PCR0_t> & pcr0, const std::shared_ptr<IOPDmacRegister_PCR1_t> & pcr1) :
mPCR0(pcr0),
mPCR1(pcr1)
{
}
u32 IOPDmacRegister_PCRW_t::getChannelPriority(const Context_t context, const IOPDmacChannel_t * channel)
{
// Channels 0-6 belong to PCR0, channels 7-13 belong to PCR1.
if (channel->getChannelID() >= 0 && channel->getChannelID() < 7)
return mPCR0->getFieldValue(context, IOPDmacRegister_PCR0_t::Fields::CHANNEL_PRIORITY_KEYS[channel->getChannelID()]);
else if (channel->getChannelID() >= 7 && channel->getChannelID() < 14)
return mPCR1->getFieldValue(context, IOPDmacRegister_PCR1_t::Fields::CHANNEL_PRIORITY_KEYS[channel->getChannelID() % 7]);
else
throw std::runtime_error("IOP DMAC PCRW could not determine channel get priority index.");
}
void IOPDmacRegister_PCRW_t::setChannelPriority(const Context_t context, const IOPDmacChannel_t * channel, const u32 value)
{
// Channels 0-6 belong to PCR0, channels 7-13 belong to PCR1.
if (channel->getChannelID() >= 0 && channel->getChannelID() < 7)
mPCR0->setFieldValue(context, IOPDmacRegister_PCR0_t::Fields::CHANNEL_PRIORITY_KEYS[channel->getChannelID()], value);
else if (channel->getChannelID() >= 7 && channel->getChannelID() < 14)
mPCR1->setFieldValue(context, IOPDmacRegister_PCR1_t::Fields::CHANNEL_PRIORITY_KEYS[channel->getChannelID() % 7], value);
else
throw std::runtime_error("IOP DMAC PCRW could not determine channel set priority index.");
}
bool IOPDmacRegister_PCRW_t::isChannelEnabled(const Context_t context, const IOPDmacChannel_t * channel)
{
// Channels 0-6 belong to PCR0, channels 7-13 belong to PCR1.
if (channel->getChannelID() >= 0 && channel->getChannelID() < 7)
return (mPCR0->getFieldValue(context, IOPDmacRegister_PCR0_t::Fields::CHANNEL_ENABLE_KEYS[channel->getChannelID()]) > 0);
else if (channel->getChannelID() >= 7 && channel->getChannelID() < 14)
return (mPCR1->getFieldValue(context, IOPDmacRegister_PCR1_t::Fields::CHANNEL_ENABLE_KEYS[channel->getChannelID() % 7]) > 0);
else
throw std::runtime_error("IOP DMAC PCRW could not determine channel get enabled index.");
}
void IOPDmacRegister_PCRW_t::setChannelEnabled(const Context_t context, const IOPDmacChannel_t * channel, const bool value)
{
// Channels 0-6 belong to PCR0, channels 7-13 belong to PCR1.
if (channel->getChannelID() >= 0 && channel->getChannelID() < 7)
mPCR0->setFieldValue(context, IOPDmacRegister_PCR0_t::Fields::CHANNEL_ENABLE_KEYS[channel->getChannelID()], (value ? 1 : 0));
else if (channel->getChannelID() >= 7 && channel->getChannelID() < 14)
mPCR1->setFieldValue(context, IOPDmacRegister_PCR1_t::Fields::CHANNEL_ENABLE_KEYS[channel->getChannelID() % 7], (value ? 1: 0));
else
throw std::runtime_error("IOP DMAC PCRW could not determine channel set enabled index.");
}
IOPDmacRegister_ICRW_t::IOPDmacRegister_ICRW_t(const std::shared_ptr<IOPDmacRegister_ICR0_t> & icr0, const std::shared_ptr<IOPDmacRegister_ICR1_t> & icr1) :
mICR0(icr0),
mICR1(icr1)
{
}
u32 IOPDmacRegister_ICRW_t::getChannelIRM(const Context_t context, const IOPDmacChannel_t * channel)
{
// Channels 0-6 belong to ICR0.
if (channel->getChannelID() >= 0 && channel->getChannelID() < 7)
return mICR0->getFieldValue(context, IOPDmacRegister_ICR0_t::Fields::CHANNEL_IRM_KEYS[channel->getChannelID()]);
else
throw std::runtime_error("IOP DMAC PCRW could not determine channel get IRM index.");
}
void IOPDmacRegister_ICRW_t::setChannelIRM(const Context_t context, const IOPDmacChannel_t * channel, const u32 value)
{
// Channels 0-6 belong to ICR0.
if (channel->getChannelID() >= 0 && channel->getChannelID() < 7)
mICR0->setFieldValue(context, IOPDmacRegister_ICR0_t::Fields::CHANNEL_IRM_KEYS[channel->getChannelID()], value);
else
throw std::runtime_error("IOP DMAC PCRW could not determine channel set IRM index.");
}
u32 IOPDmacRegister_ICRW_t::getChannelIQE(const Context_t context, const IOPDmacChannel_t * channel)
{
// Channels 0-13 belong to ICR1.
if (channel->getChannelID() >= 0 && channel->getChannelID() < 14)
return mICR1->getFieldValue(context, IOPDmacRegister_ICR1_t::Fields::CHANNEL_IQE_KEYS[channel->getChannelID()]);
else
throw std::runtime_error("IOP DMAC PCRW could not determine channel get IQE index.");
}
void IOPDmacRegister_ICRW_t::setChannelIQE(const Context_t context, const IOPDmacChannel_t * channel, const u32 value)
{
// Channels 0-13 belong to ICR1.
if (channel->getChannelID() >= 0 && channel->getChannelID() < 14)
mICR1->setFieldValue(context, IOPDmacRegister_ICR1_t::Fields::CHANNEL_IQE_KEYS[channel->getChannelID()], value);
else
throw std::runtime_error("IOP DMAC PCRW could not determine channel set IQE index.");
}
u32 IOPDmacRegister_ICRW_t::getChannelTCM(const Context_t context, const IOPDmacChannel_t * channel)
{
// Channels 0-6 belong to ICR0, channels 7-13 belong to ICR1.
if (channel->getChannelID() >= 0 && channel->getChannelID() < 7)
return mICR0->getFieldValue(context, IOPDmacRegister_ICR0_t::Fields::CHANNEL_TCM_KEYS[channel->getChannelID()]);
else if (channel->getChannelID() >= 7 && channel->getChannelID() < 14)
return mICR1->getFieldValue(context, IOPDmacRegister_ICR1_t::Fields::CHANNEL_TCM_KEYS[channel->getChannelID() % 7]);
else
throw std::runtime_error("IOP DMAC PCRW could not determine channel get TCM index.");
}
void IOPDmacRegister_ICRW_t::setChannelTCM(const Context_t context, const IOPDmacChannel_t * channel, const u32 value)
{
// Channels 0-6 belong to ICR0, channels 7-13 belong to ICR1.
if (channel->getChannelID() >= 0 && channel->getChannelID() < 7)
mICR0->setFieldValue(context, IOPDmacRegister_ICR0_t::Fields::CHANNEL_TCM_KEYS[channel->getChannelID()], value);
else if (channel->getChannelID() >= 7 && channel->getChannelID() < 14)
mICR1->setFieldValue(context, IOPDmacRegister_ICR1_t::Fields::CHANNEL_TCM_KEYS[channel->getChannelID() % 7], value);
else
throw std::runtime_error("IOP DMAC PCRW could not determine channel set TCM index.");
}
u32 IOPDmacRegister_ICRW_t::getChannelTCI(const Context_t context, const IOPDmacChannel_t * channel)
{
// Channels 0-6 belong to ICR0, channels 7-13 belong to ICR1.
if (channel->getChannelID() >= 0 && channel->getChannelID() < 7)
return mICR0->getFieldValue(context, IOPDmacRegister_ICR0_t::Fields::CHANNEL_TCI_KEYS[channel->getChannelID()]);
else if (channel->getChannelID() >= 7 && channel->getChannelID() < 14)
return mICR1->getFieldValue(context, IOPDmacRegister_ICR1_t::Fields::CHANNEL_TCI_KEYS[channel->getChannelID() % 7]);
else
throw std::runtime_error("IOP DMAC PCRW could not determine channel get TCI index.");
}
void IOPDmacRegister_ICRW_t::setChannelTCI(const Context_t context, const IOPDmacChannel_t * channel, const u32 value)
{
// Channels 0-6 belong to ICR0, channels 7-13 belong to ICR1.
if (channel->getChannelID() >= 0 && channel->getChannelID() < 7)
mICR0->setFieldValue(context, IOPDmacRegister_ICR0_t::Fields::CHANNEL_TCI_KEYS[channel->getChannelID()], value);
else if (channel->getChannelID() >= 7 && channel->getChannelID() < 14)
mICR1->setFieldValue(context, IOPDmacRegister_ICR1_t::Fields::CHANNEL_TCI_KEYS[channel->getChannelID() % 7], value);
else
throw std::runtime_error("IOP DMAC PCRW could not determine channel set TCI index.");
}
bool IOPDmacRegister_ICRW_t::isInterruptPending(const Context_t context) const
{
return (mICR0->isInterruptPending(context) || mICR1->isInterruptPending(context));
}

View file

@ -1,9 +1,12 @@
#pragma once
#include <memory>
#include "Common/Global/Globals.h"
#include "Common/Types/Register/BitfieldRegister32_t.h"
#include <memory>
class IOPDmacChannel_t;
/*
The IOP DMAC PCR0 register.
@ -35,6 +38,7 @@ public:
static constexpr int PriorityCPU = 14;
static constexpr int EnableCPU = 15;
static constexpr int CHANNEL_PRIORITY_KEYS[Constants::IOP::DMAC::NUMBER_DMAC_CHANNELS / 2] = { Priority0, Priority1, Priority2, Priority3, Priority4, Priority5, Priority6 };
static constexpr int CHANNEL_ENABLE_KEYS[Constants::IOP::DMAC::NUMBER_DMAC_CHANNELS / 2] = { Enable0, Enable1, Enable2, Enable3, Enable4, Enable5, Enable6 };
};
@ -126,8 +130,8 @@ public:
static constexpr int Enable12 = 11;
static constexpr int Priority13 = 12;
static constexpr int Enable13 = 13;
static constexpr int CHANNEL_PRIORITY_KEYS[Constants::IOP::DMAC::NUMBER_DMAC_CHANNELS / 2] = { Priority7, Priority8, Priority9, Priority10, Priority11, Priority12, Priority13 };
static constexpr int CHANNEL_ENABLE_KEYS[Constants::IOP::DMAC::NUMBER_DMAC_CHANNELS / 2] = { Enable7, Enable8, Enable9, Enable10, Enable11, Enable12, Enable13 };
};
@ -199,4 +203,103 @@ private:
Reference to the ICR0 register, see handleInterruptCheck() above.
*/
std::shared_ptr<IOPDmacRegister_ICR0_t> mICR0;
};
};
/*
Wrapper class around the PCR 0 and 1 registers to provide one interface for all channels.
This uses the register functionality, and uses values internally in terms of u32's.
Provides:
- Get/set for priorities.
- Get/set for enabling.
TODO: CPU priority and enabling not implemented, just use PCR0 directly.
For more information, see the PCR0 and PCR1 registers above.
*/
class IOPDmacRegister_PCRW_t
{
public:
IOPDmacRegister_PCRW_t(const std::shared_ptr<IOPDmacRegister_PCR0_t> & pcr0, const std::shared_ptr<IOPDmacRegister_PCR1_t> & pcr1);
/*
Gets or sets the priority status for a channel.
*/
u32 getChannelPriority(const Context_t context, const IOPDmacChannel_t * channel);
void setChannelPriority(const Context_t context, const IOPDmacChannel_t * channel, const u32 value);
/*
Gets or sets the enabled status for a channel.
*/
bool isChannelEnabled(const Context_t context, const IOPDmacChannel_t * channel);
void setChannelEnabled(const Context_t context, const IOPDmacChannel_t * channel, const bool value);
private:
/*
Reference to the PCR0 register.
*/
std::shared_ptr<IOPDmacRegister_PCR0_t> mPCR0;
/*
Reference to the PCR1 register.
*/
std::shared_ptr<IOPDmacRegister_PCR1_t> mPCR1;
};
/*
Wrapper class around the ICR 0 and 1 registers to provide one interface for all channels.
This uses the register functionality, and uses values internally in terms of u32's.
Provides:
- Get/set for IRM (individual request mask).
- Get/set for IQE (interrupt on tag event queue).
- Get/set for TCM (transfer complete mask).
- Get/set for TCI (transfer complete interrupt).
- Getting the interrupted status (checks for any channels that have interrupted).
For more information, see the ICR0 and ICR1 registers above.
*/
class IOPDmacRegister_ICRW_t
{
public:
IOPDmacRegister_ICRW_t(const std::shared_ptr<IOPDmacRegister_ICR0_t> & icr0, const std::shared_ptr<IOPDmacRegister_ICR1_t> & icr1);
/*
Gets or sets the IRM for a channel.
Only channels 0 to 6 are valid for IRM.
TODO: Not sure where the others are for 7 to 13...
*/
u32 getChannelIRM(const Context_t context, const IOPDmacChannel_t * channel);
void setChannelIRM(const Context_t context, const IOPDmacChannel_t * channel, const u32 value);
/*
Gets or sets the IQE for a channel.
*/
u32 getChannelIQE(const Context_t context, const IOPDmacChannel_t * channel);
void setChannelIQE(const Context_t context, const IOPDmacChannel_t * channel, const u32 value);
/*
Gets or sets the TCM for a channel.
*/
u32 getChannelTCM(const Context_t context, const IOPDmacChannel_t * channel);
void setChannelTCM(const Context_t context, const IOPDmacChannel_t * channel, const u32 value);
/*
Gets or sets the TCI for a channel.
*/
u32 getChannelTCI(const Context_t context, const IOPDmacChannel_t * channel);
void setChannelTCI(const Context_t context, const IOPDmacChannel_t * channel, const u32 value);
/*
Returns if any of the channels are in an interrupt pending state.
*/
bool isInterruptPending(const Context_t context) const;
private:
/*
Reference to the PCR0 register.
*/
std::shared_ptr<IOPDmacRegister_ICR0_t> mICR0;
/*
Reference to the PCR1 register.
*/
std::shared_ptr<IOPDmacRegister_ICR1_t> mICR1;
};

View file

@ -80,9 +80,11 @@ Resources_t::Resources_t() :
{
postInit_EE_DMAC();
postInit_EE_VPU_VU();
postInit_SPU2();
postInit_IOP_DMAC();
// SPU2 dependant on IOP DMAC resources initialisation.
postInit_SPU2();
// Put these last - they are dependant on the resources initialised from the other init functions (due to memory mappings).
postInit_EE();
postInit_IOP();
@ -1568,8 +1570,8 @@ void Resources_t::postInit_IOP() const
void Resources_t::postInit_SPU2() const
{
SPU2->CORE_0 = std::make_shared<SPU2Core_C0_t>(Common->FIFO_SPU2C0);
SPU2->CORE_1 = std::make_shared<SPU2Core_C1_t>(Common->FIFO_SPU2C1);
SPU2->CORE_0 = std::make_shared<SPU2Core_C0_t>(IOP->DMAC->CHANNEL_SPU2C0);
SPU2->CORE_1 = std::make_shared<SPU2Core_C1_t>(IOP->DMAC->CHANNEL_SPU2C1);
SPU2->CORES[0] = SPU2->CORE_0;
SPU2->CORES[1] = SPU2->CORE_1;
}

View file

@ -90,3 +90,20 @@ SPU2CoreRegister_STATX_t::SPU2CoreRegister_STATX_t(const char* mnemonic, const b
{
registerField(Fields::NeedData, "NeedData", 7, 1, 0);
}
SPU2CoreRegister_ADMAS_t::SPU2CoreRegister_ADMAS_t(const char * mnemonic, const bool debugReads, const bool debugWrites, const int coreID) :
Register16_t(mnemonic, debugReads, debugWrites),
mCoreID(coreID)
{
}
bool SPU2CoreRegister_ADMAS_t::isADMAEnabled(const Context_t context)
{
return ((mCoreID + 1) & readHword(context)) > 0;
}
void SPU2CoreRegister_ADMAS_t::setADMAOff(const Context_t context)
{
writeHword(context, readHword(context) | (~0x3));
}

View file

@ -146,4 +146,33 @@ public:
};
SPU2CoreRegister_STATX_t(const char * mnemonic, const bool debugReads, const bool debugWrites);
};
};
/*
The SPU2 Core ADMAS register.
Controls the Auto DMA function.
When turning ADMA off, it requires a special value to be set.
*/
class SPU2CoreRegister_ADMAS_t : public Register16_t
{
public:
SPU2CoreRegister_ADMAS_t(const char * mnemonic, const bool debugReads, const bool debugWrites, const int coreID);
/*
Returns if ADMA is enabled for this SPU2 core.
Requires the core ID to perform this check.
*/
bool isADMAEnabled(const Context_t context);
/*
Turns off ADMA by setting a magic value.
TODO: Confirm special value is actually needed, it is in SPU2-X, but not checked against BIOS code.
*/
void setADMAOff(const Context_t context);
private:
/*
The associated core ID.
*/
int mCoreID;
};

View file

@ -5,8 +5,9 @@
#include "Resources/SPU2/Types/SPU2Cores_t.h"
#include "Resources/SPU2/Types/SPU2CoreRegisters_t.h"
#include "Resources/SPU2/Types/SPU2CoreVoices_t.h"
#include "Resources/IOP/DMAC/Types/IOPDmacChannels_t.h"
SPU2Core_t::SPU2Core_t(const int coreID, const std::shared_ptr<FIFOQueue_t> & fifoQueue) :
SPU2Core_t::SPU2Core_t(const int coreID, const std::shared_ptr<IOPDmacChannel_t> & dmaChannel) :
PMON0(nullptr),
PMON1(nullptr),
NON0(nullptr),
@ -128,7 +129,8 @@ SPU2Core_t::SPU2Core_t(const int coreID, const std::shared_ptr<FIFOQueue_t> & fi
VOICE_22(nullptr),
VOICE_23(nullptr),
VOICES{ nullptr },
FIFOQueue(fifoQueue),
DMAChannel(dmaChannel),
FIFOQueue(dmaChannel->FIFOQueue),
mCoreID(coreID)
{
}
@ -143,13 +145,8 @@ const SPU2CoreTable::SPU2CoreInfo_t* SPU2Core_t::getInfo() const
return SPU2CoreTable::getInfo(this);
}
bool SPU2Core_t::isADMAEnabled(const Context_t context) const
{
return ((getCoreID() + 1) & ADMAS->readHword(context)) > 0;
}
SPU2Core_C0_t::SPU2Core_C0_t(const std::shared_ptr<FIFOQueue_t> & fifoQueue) :
SPU2Core_t(CORE_ID, fifoQueue)
SPU2Core_C0_t::SPU2Core_C0_t(const std::shared_ptr<IOPDmacChannel_t> & dmaChannel) :
SPU2Core_t(CORE_ID, dmaChannel)
{
PMON0 = std::make_shared<SPU2CoreRegister_CHAN0_t>("SPU2 C0 PMON0", false, false);
PMON1 = std::make_shared<SPU2CoreRegister_CHAN1_t>("SPU2 C0 PMON1", false, false);
@ -175,7 +172,7 @@ SPU2Core_C0_t::SPU2Core_C0_t(const std::shared_ptr<FIFOQueue_t> & fifoQueue) :
TSAL = std::make_shared<PairRegister16_t>("SPU2 C0 TSAL", false, true, TSAH);
DATA0 = std::make_shared<Register16_t>("SPU2 C0 DATA0", false, false);
DATA1 = std::make_shared<Register16_t>("SPU2 C0 DATA1", false, false);
ADMAS = std::make_shared<Register16_t>("SPU2 C0 ADMAS", false, true);
ADMAS = std::make_shared<SPU2CoreRegister_ADMAS_t>("SPU2 C0 ADMAS", false, true, CORE_ID);
ESAH = std::make_shared<Register16_t>("SPU2 C0 ESAH", false, false);
ESAL = std::make_shared<Register16_t>("SPU2 C0 ESAL", false, false);
APF1_SIZEH = std::make_shared<Register16_t>("SPU2 C0 APF1_SIZEH", false, false);
@ -297,8 +294,8 @@ SPU2Core_C0_t::SPU2Core_C0_t(const std::shared_ptr<FIFOQueue_t> & fifoQueue) :
VOICES[23] = VOICE_23;
}
SPU2Core_C1_t::SPU2Core_C1_t(const std::shared_ptr<FIFOQueue_t> & fifoQueue):
SPU2Core_t(CORE_ID, fifoQueue)
SPU2Core_C1_t::SPU2Core_C1_t(const std::shared_ptr<IOPDmacChannel_t> & dmaChannel):
SPU2Core_t(CORE_ID, dmaChannel)
{
PMON0 = std::make_shared<SPU2CoreRegister_CHAN0_t>("SPU2 C1 PMON0", false, false);
PMON1 = std::make_shared<SPU2CoreRegister_CHAN1_t>("SPU2 C1 PMON1", false, false);
@ -324,7 +321,7 @@ SPU2Core_C1_t::SPU2Core_C1_t(const std::shared_ptr<FIFOQueue_t> & fifoQueue):
TSAL = std::make_shared<PairRegister16_t>("SPU2 C1 TSAL", false, true, TSAH);
DATA0 = std::make_shared<Register16_t>("SPU2 C1 DATA0", false, false);
DATA1 = std::make_shared<Register16_t>("SPU2 C1 DATA1", false, false);
ADMAS = std::make_shared<Register16_t>("SPU2 C1 ADMAS", false, true);
ADMAS = std::make_shared<SPU2CoreRegister_ADMAS_t>("SPU2 C1 ADMAS", false, true, CORE_ID);
ESAH = std::make_shared<Register16_t>("SPU2 C1 ESAH", false, false);
ESAL = std::make_shared<Register16_t>("SPU2 C1 ESAL", false, false);
APF1_SIZEH = std::make_shared<Register16_t>("SPU2 C1 APF1_SIZEH", false, false);

View file

@ -13,10 +13,12 @@ class SPU2CoreRegister_MMIX_t;
class SPU2CoreRegister_ATTR_t;
class SPU2CoreRegister_VOL_t;
class SPU2CoreRegister_STATX_t;
class SPU2CoreRegister_ADMAS_t;
class ByteMemory_t;
class Register16_t;
class PairRegister16_t;
class FIFOQueue_t;
class IOPDmacChannel_t;
/*
Base class representing a SPU2 core.
@ -25,7 +27,7 @@ There are 2 individual cores in the SPU2, each with 24 voice channels.
class SPU2Core_t
{
public:
SPU2Core_t(const int coreID, const std::shared_ptr<FIFOQueue_t> & fifoQueue);
SPU2Core_t(const int coreID, const std::shared_ptr<IOPDmacChannel_t> & dmaChannel);
/*
SPU2 Core registers.
@ -54,7 +56,7 @@ public:
std::shared_ptr<PairRegister16_t> TSAL;
std::shared_ptr<Register16_t> DATA0;
std::shared_ptr<Register16_t> DATA1;
std::shared_ptr<Register16_t> ADMAS; // "AutoDMA Status".
std::shared_ptr<SPU2CoreRegister_ADMAS_t> ADMAS; // "AutoDMA Status".
std::shared_ptr<Register16_t> ESAH;
std::shared_ptr<Register16_t> ESAL;
std::shared_ptr<Register16_t> APF1_SIZEH;
@ -156,11 +158,21 @@ public:
std::shared_ptr<SPU2CoreVoice_t> VOICE_23;
std::shared_ptr<SPU2CoreVoice_t> VOICES[Constants::SPU2::NUMBER_CORE_VOICES];
/*
Associated IOP DMA channel attached to this core.
When ADMA modes are used, this is used to stop trasnfers from the IOP side and trigger an IOP DMAC interrupt.
TODO: Confirm this is how it works. From PCSX2, the spu2-x plugin also controls the IOP SPU2CX DMAC bits.
However, it does this for ADMA and MDMA - I think this was only meant for ADMA, since it processes data in 0x200 hword blocks only.
The way the SPU2 overview manual reads, it often implies that the SPU2 can change the IOP DMA status bits - which means hardware wiring... Maybe check schematics for clues?
*/
std::shared_ptr<IOPDmacChannel_t> DMAChannel;
/*
Associated DMA FIFO queue attached to this core.
This is the same as DMAChannel->FIFOQueue
*/
std::shared_ptr<FIFOQueue_t> FIFOQueue;
/*
Returns the core ID.
*/
@ -171,13 +183,6 @@ public:
*/
const SPU2CoreTable::SPU2CoreInfo_t * getInfo() const;
/*
Returns if the core is enabled for auto DMA transfers, by checking that ((coreIdx + 1) & ADMAS) > 0.
From PCSX2's SPU2-X dma.cpp.
TODO: Investigate more, this does not make much sense to me. The SPU2 overview manual mentions ADMA reads and writes, but only writes are ever used it seems.
*/
bool isADMAEnabled(const Context_t context) const;
private:
/*
SPU2 Core ID.
@ -188,7 +193,7 @@ private:
class SPU2Core_C0_t : public SPU2Core_t
{
public:
SPU2Core_C0_t(const std::shared_ptr<FIFOQueue_t> & fifoQueue);
SPU2Core_C0_t(const std::shared_ptr<IOPDmacChannel_t> & dmaChannel);
static constexpr int CORE_ID = 0;
};
@ -196,7 +201,7 @@ public:
class SPU2Core_C1_t : public SPU2Core_t
{
public:
SPU2Core_C1_t(const std::shared_ptr<FIFOQueue_t> & fifoQueue);
SPU2Core_C1_t(const std::shared_ptr<IOPDmacChannel_t> & dmaChannel);
static constexpr int CORE_ID = 1;
};

View file

@ -66,7 +66,8 @@ int IOPDmac_s::step(const Event_t & event)
mChannel = channel.get();
// Check if channel is enabled for transfer (both from PCR and the CHCR).
if (isChannelEnabled())
if ((mDMAC->PCRW->isChannelEnabled(getContext(), mChannel))
&& (mChannel->CHCR->getFieldValue(getContext(), IOPDmacChannelRegister_CHCR_t::Fields::Start) > 0))
{
switch (mChannel->CHCR->getLogicalMode(getContext()))
{
@ -234,8 +235,10 @@ bool IOPDmac_s::transferChain()
void IOPDmac_s::handleInterruptCheck() const
{
// Check ICR0 and ICR1 for interrupt status, else clear the master interrupt and INTC bits.
if (mDMAC->ICR0->isInterruptPending(getContext()) || mDMAC->ICR1->isInterruptPending(getContext()))
if (mDMAC->ICRW->isInterruptPending(getContext()))
{
mINTC->STAT->setFieldValue(getContext(), IOPIntcRegister_STAT_t::Fields::DMAC, 1);
}
else
{
mDMAC->ICR0->setFieldValue(getContext(), IOPDmacRegister_ICR0_t::Fields::MasterInterrupt, 0);
@ -297,32 +300,13 @@ void IOPDmac_s::setStateSuspended() const
// Stop channel.
mChannel->CHCR->setFieldValue(getContext(), IOPDmacChannelRegister_CHCR_t::Fields::Start, 0);
// Emit interrupt stat bit (use ICR0 or ICR1 based on channel index).
if (mChannel->getChannelID() < 7)
mDMAC->ICR0->setFieldValue(getContext(), IOPDmacRegister_ICR0_t::Fields::CHANNEL_TCI_KEYS[mChannel->getChannelID()], 1);
else
mDMAC->ICR1->setFieldValue(getContext(), IOPDmacRegister_ICR1_t::Fields::CHANNEL_TCI_KEYS[mChannel->getChannelID() % 7], 1);
}
bool IOPDmac_s::isChannelEnabled() const
{
if (mChannel->CHCR->getFieldValue(getContext(), IOPDmacChannelRegister_CHCR_t::Fields::Start) > 0)
{
// Channels 0-6 belong to PCR0, channels 7-13 belong to PCR1.
if (mChannel->getChannelID() < 7)
return (mDMAC->PCR0->getFieldValue(getContext(), IOPDmacRegister_PCR0_t::Fields::CHANNEL_ENABLE_KEYS[mChannel->getChannelID()]) > 0);
else
return (mDMAC->PCR1->getFieldValue(getContext(), IOPDmacRegister_PCR1_t::Fields::CHANNEL_ENABLE_KEYS[mChannel->getChannelID() % 7]) > 0);
}
else
{
return false;
}
// Set channel transfer complete interrupt bit.
mDMAC->ICRW->setChannelTCI(getContext(), mChannel, 1);
}
bool IOPDmac_s::isChannelIRQEnabled() const
{
return (mDMAC->ICR1->getFieldValue(getContext(), IOPDmacRegister_ICR1_t::Fields::CHANNEL_IQE_KEYS[mChannel->getChannelID()]) > 0);
return (mDMAC->ICRW->getChannelIQE(getContext(), mChannel) > 0);
}
u32 IOPDmac_s::readWordMemory(const u32 bytePhysicalAddress) const

View file

@ -9,10 +9,13 @@
#include "VM/Systems/SPU2/SPU2_s.h"
#include "Resources/Resources_t.h"
#include "Resources/Common/Common_t.h"
#include "Resources/IOP/IOP_t.h"
#include "Resources/IOP/INTC/IOPIntc_t.h"
#include "Resources/IOP/INTC/Types/IOPIntcRegisters_t.h"
#include "Resources/IOP/IOP_t.h"
#include "Resources/IOP/DMAC/IOPDmac_t.h"
#include "Resources/IOP/DMAC/Types/IOPDmacChannelRegisters_t.h"
#include "Resources/IOP/DMAC/Types/IOPDmacChannels_t.h"
#include "Resources/IOP/DMAC/Types/IOPDmacRegisters_t.h"
#include "Resources/SPU2/SPU2_t.h"
#include "Resources/SPU2/Types/SPU2Registers_t.h"
#include "Resources/SPU2/Types/SPU2Cores_t.h"
@ -25,6 +28,7 @@ SPU2_s::SPU2_s(VM * vm) :
{
mSPU2 = getVM()->getResources()->SPU2;
mINTC = getVM()->getResources()->IOP->INTC;
mDMAC = getVM()->getResources()->IOP->DMAC;
}
void SPU2_s::initialise()
@ -81,41 +85,32 @@ bool SPU2_s::handleDMATransfer()
throw std::runtime_error("SPU2 ATTR DMABits field set to non-zero. What is this for?");
int dmaCount = 0;
bool updateSTATX = false;
switch (mCore->ATTR->getFieldValue(getContext(), SPU2CoreRegister_ATTR_t::Fields::DMAMode))
{
case 0:
{
// Auto DMA write mode.
if (mCore->isADMAEnabled(getContext()))
{
if (mCore->ADMAS->isADMAEnabled(getContext()))
dmaCount = transferData_ADMA_Write();
updateSTATX = true;
}
break;
}
case 1:
{
// Auto DMA read mode.
if (mCore->isADMAEnabled(getContext()))
{
if (mCore->ADMAS->isADMAEnabled(getContext()))
dmaCount = transferData_ADMA_Read();
updateSTATX = true;
}
break;
}
case 2:
{
// Manual DMA write mode.
dmaCount = transferData_MDMA_Write();
updateSTATX = true;
break;
}
case 3:
{
// Manual DMA read mode.
dmaCount = transferData_MDMA_Read();
updateSTATX = true;
break;
}
default:
@ -124,15 +119,6 @@ bool SPU2_s::handleDMATransfer()
}
}
// Update STATX only if DMA attemped to run, and if there was FIFO data available.
if (updateSTATX)
{
if (mCore->FIFOQueue->getReadAvailable() > 0)
mCore->STATX->setFieldValue(getContext(), SPU2CoreRegister_STATX_t::Fields::NeedData, 0);
else
mCore->STATX->setFieldValue(getContext(), SPU2CoreRegister_STATX_t::Fields::NeedData, 1);
}
return (dmaCount > 0);
}
@ -153,17 +139,22 @@ bool SPU2_s::handleSoundGeneration()
int SPU2_s::transferData_ADMA_Write() const
{
// TODO: Check this, its probably wrong. The write addresses are also meant to be used in conjunction with the current read address (double buffer).
// TSA is not used here! The write addresses are fixed. See pages 13, 28 and 55 of the SPU2 Overview manual.
// Note: TSA is not used here! The write addresses are fixed. See pages 13, 28 and 55 of the SPU2 Overview manual.
// Check for incoming data and read it in, otherwise exit early as theres nothing to do.
u16 data;
if (!mCore->FIFOQueue->read(getContext(), reinterpret_cast<u8*>(&data), Constants::NUMBER_BYTES_IN_HWORD))
{
// TODO: check this especially!!!
// Need to also clear the STATX.NeedData bit when out of data.
mCore->STATX->setFieldValue(getContext(), SPU2CoreRegister_STATX_t::Fields::NeedData, 0);
// TODO: check this especially!!! Are we meant to stop the IOP DMA as well?
// Need to also set the STATX.NeedData bit when out of data.
mCore->STATX->setFieldValue(getContext(), SPU2CoreRegister_STATX_t::Fields::NeedData, 1);
return 0;
}
else
{
// Clear STATX, we still have data to process.
mCore->STATX->setFieldValue(getContext(), SPU2CoreRegister_STATX_t::Fields::NeedData, 0);
}
// Depending on the current transfer count, we are in the left or right sound channel data block (from SPU2-X/Dma.cpp).
// Data incoming is in a striped pattern with 0x100 hwords for the left channel, followed by 0x100 hwords for the right channel, repeated.
@ -185,15 +176,30 @@ int SPU2_s::transferData_ADMA_Write() const
else
address = mCore->getInfo()->mBaseTSARight + channelOffset;
// Make sure address is not outside 2MB limit (remember, we are addressing by hwords).
address %= 0x100000;
// Write to SPU2 memory.
writeHwordMemory(address, data);
// Increment the transfer count.
mCore->ATTR->mDMAOffset += 1;
// Stop ADMA if 0x100 hwords have been received, and also stop the IOP DMAC channel.
if ((mCore->ATTR->mDMAOffset % 0x100) == 0)
{
// Set STATX to signal we need data.
mCore->STATX->setFieldValue(getContext(), SPU2CoreRegister_STATX_t::Fields::NeedData, 1);
// Set IOP DMA channel status to stopped.
mCore->DMAChannel->CHCR->setFieldValue(getContext(), IOPDmacChannelRegister_CHCR_t::Fields::Start, 0);
// Emit interrupt stat bit (IOP DMAC will handle notifying the IOP).
mDMAC->ICRW->setChannelTCI(getContext(), mCore->DMAChannel.get(), 1);
// Set ADMA register to special value.
mCore->ADMAS->setADMAOff(getContext());
log(Debug, "SPU2 core %d reached end of ADMA block (0x100 hwords). Remaining data in FIFO queue = 0x%X.", mCore->getCoreID(), mCore->FIFOQueue->getReadAvailable());
}
// ADMA has completed one transfer.
return 1;
}

View file

@ -4,6 +4,7 @@
class SPU2_t;
class IOPIntc_t;
class IOPDmac_t;
class SPU2Core_t;
/*
@ -36,6 +37,7 @@ public:
*/
std::shared_ptr<SPU2_t> mSPU2;
std::shared_ptr<IOPIntc_t> mINTC;
std::shared_ptr<IOPDmac_t> mDMAC;
SPU2Core_t * mCore;
///////////////////////////