2010-08-31 05:22:26 +00:00
/* PCSX2 - PS2 Emulator for PCs
* Copyright ( C ) 2002 - 2010 PCSX2 Dev Team
*
* PCSX2 is free software : you can redistribute it and / or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found -
* ation , either version 3 of the License , or ( at your option ) any later version .
*
* PCSX2 is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE . See the GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along with PCSX2 .
* If not , see < http : //www.gnu.org/licenses/>.
*/
# include "PrecompiledHeader.h"
# include "Common.h"
# include "Hardware.h"
2011-08-12 02:31:49 +00:00
# include "MTVU.h"
2010-08-31 05:22:26 +00:00
2010-09-05 00:36:03 +00:00
# include "IPU/IPUdma.h"
2010-08-31 05:22:26 +00:00
# include "ps2/HwInternal.h"
2010-08-31 21:22:39 +00:00
bool DMACh : : transfer ( const char * s , tDMA_TAG * ptag )
{
if ( ptag = = NULL ) // Is ptag empty?
{
throwBusError ( s ) ;
return false ;
}
chcrTransfer ( ptag ) ;
qwcTransfer ( ptag ) ;
return true ;
}
void DMACh : : unsafeTransfer ( tDMA_TAG * ptag )
{
chcrTransfer ( ptag ) ;
qwcTransfer ( ptag ) ;
}
tDMA_TAG * DMACh : : getAddr ( u32 addr , u32 num , bool write )
{
tDMA_TAG * ptr = dmaGetAddr ( addr , write ) ;
if ( ptr = = NULL )
{
throwBusError ( " dmaGetAddr " ) ;
setDmacStat ( num ) ;
chcr . STR = false ;
}
return ptr ;
}
tDMA_TAG * DMACh : : DMAtransfer ( u32 addr , u32 num )
{
tDMA_TAG * tag = getAddr ( addr , num , false ) ;
if ( tag = = NULL ) return NULL ;
chcrTransfer ( tag ) ;
qwcTransfer ( tag ) ;
return tag ;
}
tDMA_TAG DMACh : : dma_tag ( )
{
return chcr . tag ( ) ;
}
2022-04-12 11:54:30 +00:00
std : : string DMACh : : cmq_to_str ( ) const
2010-08-31 21:22:39 +00:00
{
2022-04-12 11:54:30 +00:00
return StringUtil : : StdStringFromFormat ( " chcr = %lx, madr = %lx, qwc = %lx " , chcr . _u32 , madr , qwc ) ;
2010-08-31 21:22:39 +00:00
}
2022-04-12 11:54:30 +00:00
std : : string DMACh : : cmqt_to_str ( ) const
2010-08-31 21:22:39 +00:00
{
2022-04-12 11:54:30 +00:00
return StringUtil : : StdStringFromFormat ( " chcr = %lx, madr = %lx, qwc = %lx, tadr = %1x " , chcr . _u32 , madr , qwc , tadr ) ;
2010-08-31 21:22:39 +00:00
}
__fi void throwBusError ( const char * s )
{
Console . Error ( " %s BUSERR " , s ) ;
dmacRegs . stat . BEIS = true ;
}
__fi void setDmacStat ( u32 num )
{
dmacRegs . stat . set_flags ( 1 < < num ) ;
}
// Note: Dma addresses are guaranteed to be aligned to 16 bytes (128 bits)
2011-08-12 02:31:49 +00:00
__fi tDMA_TAG * SPRdmaGetAddr ( u32 addr , bool write )
2010-08-31 21:22:39 +00:00
{
// if (addr & 0xf) { DMA_LOG("*PCSX2*: DMA address not 128bit aligned: %8.8x", addr); }
//For some reason Getaway references SPR Memory from itself using SPR0, oh well, let it i guess...
if ( ( addr & 0x70000000 ) = = 0x70000000 )
{
return ( tDMA_TAG * ) & eeMem - > Scratch [ addr & 0x3ff0 ] ;
}
// FIXME: Why??? DMA uses physical addresses
addr & = 0x1ffffff0 ;
2010-10-22 16:23:52 +00:00
if ( addr < Ps2MemSize : : MainRam )
2010-08-31 21:22:39 +00:00
{
return ( tDMA_TAG * ) & eeMem - > Main [ addr ] ;
}
else if ( addr < 0x10000000 )
{
return ( tDMA_TAG * ) ( write ? eeMem - > ZeroWrite : eeMem - > ZeroRead ) ;
}
2012-12-16 14:23:47 +00:00
else if ( ( addr > = 0x11000000 ) & & ( addr < 0x11010000 ) )
2010-08-31 21:22:39 +00:00
{
2012-12-21 00:46:16 +00:00
if ( addr > = 0x11008000 & & THREAD_VU1 )
{
DevCon . Warning ( " MTVU: SPR Accessing VU1 Memory " ) ;
2011-08-12 02:31:49 +00:00
vu1Thread . WaitVU ( ) ;
}
2012-12-21 00:46:16 +00:00
2010-08-31 21:22:39 +00:00
//Access for VU Memory
2012-11-11 19:18:50 +00:00
if ( ( addr > = 0x1100c000 ) & & ( addr < 0x11010000 ) )
2012-12-16 14:23:47 +00:00
{
2012-12-19 20:20:14 +00:00
//DevCon.Warning("VU1 Mem %x", addr);
return ( tDMA_TAG * ) ( VU1 . Mem + ( addr & 0x3ff0 ) ) ;
2012-12-16 14:23:47 +00:00
}
2012-11-12 13:13:29 +00:00
2012-12-19 20:20:14 +00:00
if ( ( addr > = 0x11004000 ) & & ( addr < 0x11008000 ) )
{
//DevCon.Warning("VU0 Mem %x", addr);
return ( tDMA_TAG * ) ( VU0 . Mem + ( addr & 0xff0 ) ) ;
}
2012-11-12 13:13:29 +00:00
//Possibly not needed but the manual doesn't say SPR cannot access it.
2012-12-19 20:20:14 +00:00
if ( ( addr > = 0x11000000 ) & & ( addr < 0x11004000 ) )
2012-12-16 14:23:47 +00:00
{
2012-12-19 20:20:14 +00:00
//DevCon.Warning("VU0 Micro %x", addr);
return ( tDMA_TAG * ) ( VU0 . Micro + ( addr & 0xff0 ) ) ;
2012-12-16 14:23:47 +00:00
}
2012-11-11 19:18:50 +00:00
2012-12-19 20:20:14 +00:00
if ( ( addr > = 0x11008000 ) & & ( addr < 0x1100c000 ) )
2012-12-16 14:23:47 +00:00
{
2012-12-19 20:20:14 +00:00
//DevCon.Warning("VU1 Micro %x", addr);
return ( tDMA_TAG * ) ( VU1 . Micro + ( addr & 0x3ff0 ) ) ;
2012-12-16 14:23:47 +00:00
}
2012-11-11 19:18:50 +00:00
2012-12-19 20:20:14 +00:00
2012-11-23 19:56:06 +00:00
// Unreachable
return NULL ;
2010-08-31 21:22:39 +00:00
}
else
{
Console . Error ( " *PCSX2*: DMA error: %8.8x " , addr ) ;
return NULL ;
}
}
// Note: Dma addresses are guaranteed to be aligned to 16 bytes (128 bits)
__ri tDMA_TAG * dmaGetAddr ( u32 addr , bool write )
{
// if (addr & 0xf) { DMA_LOG("*PCSX2*: DMA address not 128bit aligned: %8.8x", addr); }
if ( DMA_TAG ( addr ) . SPR ) return ( tDMA_TAG * ) & eeMem - > Scratch [ addr & 0x3ff0 ] ;
// FIXME: Why??? DMA uses physical addresses
addr & = 0x1ffffff0 ;
2010-10-22 16:23:52 +00:00
if ( addr < Ps2MemSize : : MainRam )
2010-08-31 21:22:39 +00:00
{
return ( tDMA_TAG * ) & eeMem - > Main [ addr ] ;
}
else if ( addr < 0x10000000 )
{
return ( tDMA_TAG * ) ( write ? eeMem - > ZeroWrite : eeMem - > ZeroRead ) ;
}
else if ( addr < 0x10004000 )
{
// Secret scratchpad address for DMA = end of maximum main memory?
//Console.Warning("Writing to the scratchpad without the SPR flag set!");
return ( tDMA_TAG * ) & eeMem - > Scratch [ addr & 0x3ff0 ] ;
}
else
{
Console . Error ( " *PCSX2*: DMA error: %8.8x " , addr ) ;
return NULL ;
}
}
2010-08-31 05:22:26 +00:00
// Returns true if the DMA is enabled and executed successfully. Returns false if execution
// was blocked (DMAE or master DMA enabler).
static bool QuickDmaExec ( void ( * func ) ( ) , u32 mem )
{
bool ret = false ;
2010-08-31 16:40:25 +00:00
DMACh & reg = ( DMACh & ) psHu32 ( mem ) ;
2010-08-31 05:22:26 +00:00
2010-08-31 16:40:25 +00:00
if ( reg . chcr . STR & & dmacRegs . ctrl . DMAE & & ! psHu8 ( DMAC_ENABLER + 2 ) )
2010-08-31 05:22:26 +00:00
{
func ( ) ;
ret = true ;
}
return ret ;
}
static tDMAC_QUEUE QueuedDMA ( 0 ) ;
static u32 oldvalue = 0 ;
static void StartQueuedDMA ( )
{
if ( QueuedDMA . VIF0 ) { DMA_LOG ( " Resuming DMA for VIF0 " ) ; QueuedDMA . VIF0 = ! QuickDmaExec ( dmaVIF0 , D0_CHCR ) ; }
if ( QueuedDMA . VIF1 ) { DMA_LOG ( " Resuming DMA for VIF1 " ) ; QueuedDMA . VIF1 = ! QuickDmaExec ( dmaVIF1 , D1_CHCR ) ; }
if ( QueuedDMA . GIF ) { DMA_LOG ( " Resuming DMA for GIF " ) ; QueuedDMA . GIF = ! QuickDmaExec ( dmaGIF , D2_CHCR ) ; }
if ( QueuedDMA . IPU0 ) { DMA_LOG ( " Resuming DMA for IPU0 " ) ; QueuedDMA . IPU0 = ! QuickDmaExec ( dmaIPU0 , D3_CHCR ) ; }
if ( QueuedDMA . IPU1 ) { DMA_LOG ( " Resuming DMA for IPU1 " ) ; QueuedDMA . IPU1 = ! QuickDmaExec ( dmaIPU1 , D4_CHCR ) ; }
if ( QueuedDMA . SIF0 ) { DMA_LOG ( " Resuming DMA for SIF0 " ) ; QueuedDMA . SIF0 = ! QuickDmaExec ( dmaSIF0 , D5_CHCR ) ; }
if ( QueuedDMA . SIF1 ) { DMA_LOG ( " Resuming DMA for SIF1 " ) ; QueuedDMA . SIF1 = ! QuickDmaExec ( dmaSIF1 , D6_CHCR ) ; }
if ( QueuedDMA . SIF2 ) { DMA_LOG ( " Resuming DMA for SIF2 " ) ; QueuedDMA . SIF2 = ! QuickDmaExec ( dmaSIF2 , D7_CHCR ) ; }
if ( QueuedDMA . SPR0 ) { DMA_LOG ( " Resuming DMA for SPR0 " ) ; QueuedDMA . SPR0 = ! QuickDmaExec ( dmaSPR0 , D8_CHCR ) ; }
if ( QueuedDMA . SPR1 ) { DMA_LOG ( " Resuming DMA for SPR1 " ) ; QueuedDMA . SPR1 = ! QuickDmaExec ( dmaSPR1 , D9_CHCR ) ; }
}
static __ri void DmaExec ( void ( * func ) ( ) , u32 mem , u32 value )
{
2010-08-31 16:40:25 +00:00
DMACh & reg = ( DMACh & ) psHu32 ( mem ) ;
2010-08-31 05:22:26 +00:00
tDMA_CHCR chcr ( value ) ;
//It's invalid for the hardware to write a DMA while it is active, not without Suspending the DMAC
2010-08-31 16:40:25 +00:00
if ( reg . chcr . STR )
2010-08-31 05:22:26 +00:00
{
const uint channel = ChannelNumber ( mem ) ;
2012-09-13 19:55:25 +00:00
//As the manual states "Fields other than STR can only be written to when the DMA is stopped"
//Also "The DMA may not stop properly just by writing 0 to STR"
//So the presumption is that STR can be written to (ala force stop the DMA) but nothing else
//If the developer wishes to alter any of the other fields, it must be done AFTER the STR has been written,
//it will not work before or during this event.
if ( chcr . STR = = 0 )
2010-08-31 05:22:26 +00:00
{
2012-09-13 19:55:25 +00:00
//DevCon.Warning(L"32bit Force Stopping %s (Current CHCR %x) while DMA active", ChcrName(mem), reg.chcr._u32, chcr._u32);
reg . chcr . STR = 0 ;
//We need to clear any existing DMA loops that are in progress else they will continue!
if ( channel = = 1 )
2010-08-31 05:22:26 +00:00
{
2012-09-13 19:55:25 +00:00
cpuClearInt ( 10 ) ;
QueuedDMA . _u16 & = ~ ( 1 < < 10 ) ; //Clear any queued DMA requests for this channel
2010-08-31 05:22:26 +00:00
}
2012-09-13 19:55:25 +00:00
else if ( channel = = 2 )
2010-08-31 05:22:26 +00:00
{
2012-09-13 19:55:25 +00:00
cpuClearInt ( 11 ) ;
QueuedDMA . _u16 & = ~ ( 1 < < 11 ) ; //Clear any queued DMA requests for this channel
2010-08-31 05:22:26 +00:00
}
2012-09-13 19:55:25 +00:00
cpuClearInt ( channel ) ;
QueuedDMA . _u16 & = ~ ( 1 < < channel ) ; //Clear any queued DMA requests for this channel
2010-08-31 05:22:26 +00:00
}
2012-09-13 19:55:25 +00:00
//else DevCon.Warning(L"32bit Attempted to change %s CHCR (Currently %x) with %x while DMA active, ignoring QWC = %x", ChcrName(mem), reg.chcr._u32, chcr._u32, reg.qwc);
return ;
2010-08-31 05:22:26 +00:00
}
2010-08-31 16:40:25 +00:00
//if(reg.chcr.TAG != chcr.TAG && chcr.MOD == CHAIN_MODE) DevCon.Warning(L"32bit CHCR Tag on %s changed to %x from %x QWC = %x Channel Not Active", ChcrName(mem), chcr.TAG, reg.chcr.TAG, reg.qwc);
2010-08-31 05:22:26 +00:00
2010-08-31 16:40:25 +00:00
reg . chcr . set ( value ) ;
2010-08-31 05:22:26 +00:00
2014-02-27 22:22:28 +00:00
//Final Fantasy XII sets the DMA Mode to 3 which doesn't exist. On some channels (like SPR) this will break logic completely. so lets assume they mean chain.
if ( reg . chcr . MOD = = 0x3 )
{
2014-11-21 21:55:35 +00:00
static bool warned ; //Check if the warning has already been output to console, to prevent constant spam.
if ( ! warned )
{
2022-05-18 13:27:23 +00:00
DevCon . Warning ( " %s CHCR.MOD set to 3, assuming 1 (chain) " , ChcrName ( mem ) ) ;
2014-11-21 21:55:35 +00:00
warned = true ;
}
2014-02-27 22:22:28 +00:00
reg . chcr . MOD = 0x1 ;
}
2020-11-29 01:29:57 +00:00
// As tested on hardware, if NORMAL mode is started with 0 QWC it will actually transfer 1 QWC then underflows and transfer another 0xFFFF QWC's
// The easiest way to handle this is to just say 0x10000 QWC
if ( reg . chcr . STR & & ! reg . chcr . MOD & & reg . qwc = = 0 )
reg . qwc = 0x10000 ;
2010-08-31 16:40:25 +00:00
if ( reg . chcr . STR & & dmacRegs . ctrl . DMAE & & ! psHu8 ( DMAC_ENABLER + 2 ) )
2010-08-31 05:22:26 +00:00
{
func ( ) ;
}
2010-08-31 16:40:25 +00:00
else if ( reg . chcr . STR )
2010-08-31 05:22:26 +00:00
{
//DevCon.Warning(L"32bit %s DMA Start while DMAC Disabled\n", ChcrName(mem));
QueuedDMA . _u16 | = ( 1 < < ChannelNumber ( mem ) ) ; //Queue the DMA up to be started then the DMA's are Enabled and or the Suspend is lifted
} //else QueuedDMA._u16 &~= (1 << ChannelNumber(mem)); //
}
2010-08-31 21:22:39 +00:00
template < uint page >
2010-08-31 05:22:26 +00:00
__fi u32 dmacRead32 ( u32 mem )
{
2012-08-14 22:30:04 +00:00
// Fixme: OPH hack. Toggle the flag on GIF_STAT access. (rama)
2022-05-21 18:35:19 +00:00
if ( ( CHECK_OPHFLAGHACK ) & & ( page < < 12 ) = = ( mem & ( 0xf < < 12 ) ) & & ( mem = = GIF_STAT ) )
2010-08-31 21:22:39 +00:00
{
2012-08-14 22:30:04 +00:00
static unsigned counter = 1 ;
if ( + + counter = = 8 )
counter = 2 ;
// Set OPH and APATH from counter, cycling paths and alternating OPH
return gifRegs . stat . _u32 & ~ ( 7 < < 9 ) | ( counter & 1 ? counter < < 9 : 0 ) ;
2010-08-31 21:22:39 +00:00
}
return psHu32 ( mem ) ;
2010-08-31 05:22:26 +00:00
}
// Returns TRUE if the caller should do writeback of the register to eeHw; false if the
// register has no writeback, or if the writeback is handled internally.
template < uint page >
__fi bool dmacWrite32 ( u32 mem , mem32_t & value )
{
2021-01-10 14:34:55 +00:00
// DMA Writes are invalid to everything except the STR on CHCR when it is busy
// However this isn't completely confirmed and this might vary depending on if
// using chain or normal modes, DMA's may be handled internally.
// Metal Saga requires the QWC during IPU_FROM to be written but not MADR
// similar happens with Mana Khemia.
// In other cases such as Pilot Down Behind Enemy Lines, it seems to expect the DMA
// to have finished before it writes the new information, otherwise the game breaks.
if ( CHECK_DMABUSYHACK & & ( mem & 0xf0 ) & & mem > = 0x10008000 & & mem < = 0x1000E000 )
{
if ( ( psHu32 ( mem & ~ 0xff ) & 0x100 ) & & dmacRegs . ctrl . DMAE & & ! psHu8 ( DMAC_ENABLER + 2 ) )
{
2021-10-23 19:36:02 +00:00
//DevCon.Warning("Gamefix: Write to DMA addr %x while STR is busy!", mem);
2021-01-10 14:34:55 +00:00
while ( psHu32 ( mem & ~ 0xff ) & 0x100 )
{
switch ( ( mem > > 8 ) & 0xFF )
{
case 0x80 : // VIF0
vif0Interrupt ( ) ;
2021-10-23 19:36:02 +00:00
cpuRegs . interrupt & = ~ ( 1 < < DMAC_VIF0 ) ;
2021-01-10 14:34:55 +00:00
break ;
case 0x90 : // VIF1
2021-10-23 19:36:02 +00:00
if ( vif1Regs . stat . VEW )
{
vu1Finish ( false ) ;
vif1VUFinish ( ) ;
}
else
vif1Interrupt ( ) ;
cpuRegs . interrupt & = ~ ( 1 < < DMAC_VIF1 ) ;
2021-01-10 14:34:55 +00:00
break ;
case 0xA0 : // GIF
gifInterrupt ( ) ;
2021-10-23 19:36:02 +00:00
cpuRegs . interrupt & = ~ ( 1 < < DMAC_GIF ) ;
2021-01-10 14:34:55 +00:00
break ;
case 0xB0 : // IPUFROM
[[fallthrough]] ;
case 0xB4 : // IPUTO
if ( ( mem & 0xff ) = = 0x20 )
goto allow_write ; // I'm so sorry
else
return false ;
break ;
case 0xD0 : // SPRFROM
SPRFROMinterrupt ( ) ;
2021-10-23 19:36:02 +00:00
cpuRegs . interrupt & = ~ ( 1 < < DMAC_FROM_SPR ) ;
2021-01-10 14:34:55 +00:00
break ;
case 0xD4 : // SPRTO
SPRTOinterrupt ( ) ;
2021-10-23 19:36:02 +00:00
cpuRegs . interrupt & = ~ ( 1 < < DMAC_TO_SPR ) ;
2021-01-10 14:34:55 +00:00
break ;
default :
return false ;
}
}
}
allow_write : ;
}
2022-05-21 18:35:19 +00:00
switch ( mem ) {
2010-08-31 05:22:26 +00:00
2022-05-21 18:35:19 +00:00
case ( D0_QWC ) : // dma0 - vif0
case ( D1_QWC ) : // dma1 - vif1
case ( D2_QWC ) : // dma2 - gif
case ( D3_QWC ) : // dma3 - fromIPU
case ( D4_QWC ) : // dma4 - toIPU
case ( D5_QWC ) : // dma5 - sif0
case ( D6_QWC ) : // dma6 - sif1
case ( D7_QWC ) : // dma7 - sif2
case ( D8_QWC ) : // dma8 - fromSPR
case ( D9_QWC ) : // dma9 - toSPR
2020-11-29 01:29:57 +00:00
{
psHu32 ( mem ) = ( u16 ) value ;
return false ;
}
2022-05-21 18:35:19 +00:00
case ( D0_CHCR ) : // dma0 - vif0
2010-12-06 21:40:36 +00:00
{
2022-05-21 18:35:19 +00:00
DMA_LOG ( " VIF0dma EXECUTE, value=0x%x " , value ) ;
DmaExec ( dmaVIF0 , mem , value ) ;
2010-12-06 21:40:36 +00:00
return false ;
}
2010-08-31 05:22:26 +00:00
2022-05-21 18:35:19 +00:00
case ( D1_CHCR ) : // dma1 - vif1 - chcr
2020-11-29 01:29:57 +00:00
{
2022-05-21 18:35:19 +00:00
DMA_LOG ( " VIF1dma EXECUTE, value=0x%x " , value ) ;
DmaExec ( dmaVIF1 , mem , value ) ;
2020-11-29 01:29:57 +00:00
return false ;
}
2022-05-21 18:35:19 +00:00
case ( D2_CHCR ) : // dma2 - gif
2010-12-06 21:40:36 +00:00
{
DMA_LOG ( " GIFdma EXECUTE, value=0x%x " , value ) ;
DmaExec ( dmaGIF , mem , value ) ;
return false ;
}
2010-08-31 05:22:26 +00:00
2022-05-21 18:35:19 +00:00
case ( D3_CHCR ) : // dma3 - fromIPU
2010-12-06 21:40:36 +00:00
{
DMA_LOG ( " IPU0dma EXECUTE, value=0x%x \n " , value ) ;
DmaExec ( dmaIPU0 , mem , value ) ;
return false ;
}
2010-08-31 05:22:26 +00:00
2022-05-21 18:35:19 +00:00
case ( D4_CHCR ) : // dma4 - toIPU
2010-12-06 21:40:36 +00:00
{
DMA_LOG ( " IPU1dma EXECUTE, value=0x%x \n " , value ) ;
DmaExec ( dmaIPU1 , mem , value ) ;
return false ;
}
2010-08-31 05:22:26 +00:00
2022-05-21 18:35:19 +00:00
case ( D5_CHCR ) : // dma5 - sif0
2010-12-06 21:40:36 +00:00
{
DMA_LOG ( " SIF0dma EXECUTE, value=0x%x " , value ) ;
DmaExec ( dmaSIF0 , mem , value ) ;
return false ;
}
2010-08-31 05:22:26 +00:00
2022-05-21 18:35:19 +00:00
case ( D6_CHCR ) : // dma6 - sif1
2010-12-06 21:40:36 +00:00
{
DMA_LOG ( " SIF1dma EXECUTE, value=0x%x " , value ) ;
DmaExec ( dmaSIF1 , mem , value ) ;
return false ;
}
2010-08-31 05:22:26 +00:00
2022-05-21 18:35:19 +00:00
case ( D7_CHCR ) : // dma7 - sif2
2010-12-06 21:40:36 +00:00
{
DMA_LOG ( " SIF2dma EXECUTE, value=0x%x " , value ) ;
DmaExec ( dmaSIF2 , mem , value ) ;
return false ;
}
2010-08-31 05:22:26 +00:00
2022-05-21 18:35:19 +00:00
case ( D8_CHCR ) : // dma8 - fromSPR
2010-08-31 05:22:26 +00:00
{
2010-12-06 21:40:36 +00:00
DMA_LOG ( " SPR0dma EXECUTE (fromSPR), value=0x%x " , value ) ;
DmaExec ( dmaSPR0 , mem , value ) ;
return false ;
2010-08-31 05:22:26 +00:00
}
2010-12-06 21:40:36 +00:00
2022-05-21 18:35:19 +00:00
case ( D9_CHCR ) : // dma9 - toSPR
2016-01-28 20:16:20 +00:00
{
2022-05-21 18:35:19 +00:00
DMA_LOG ( " SPR1dma EXECUTE (toSPR), value=0x%x " , value ) ;
DmaExec ( dmaSPR1 , mem , value ) ;
2016-01-28 20:16:20 +00:00
return false ;
}
2022-05-21 18:35:19 +00:00
case ( fromSPR_MADR ) :
case ( toSPR_MADR ) :
2016-01-28 20:16:20 +00:00
{
// SPR bit is fixed at 0 for this channel
psHu32 ( mem ) = value & 0x7FFFFFFF ;
return false ;
}
2022-05-21 18:35:19 +00:00
case ( fromSPR_SADR ) :
case ( toSPR_SADR ) :
2016-01-27 21:23:23 +00:00
{
// Address must be QW aligned and fit in the 16K range of SPR
psHu32 ( mem ) = value & 0x3FF0 ;
return false ;
}
2022-05-21 18:35:19 +00:00
case ( DMAC_CTRL ) :
2010-08-31 05:22:26 +00:00
{
2010-12-06 21:40:36 +00:00
u32 oldvalue = psHu32 ( mem ) ;
HW_LOG ( " DMAC_CTRL Write 32bit %x " , value ) ;
psHu32 ( mem ) = value ;
//Check for DMAS that were started while the DMAC was disabled
if ( ( ( oldvalue & 0x1 ) = = 0 ) & & ( ( value & 0x1 ) = = 1 ) )
{
if ( ! QueuedDMA . empty ( ) ) StartQueuedDMA ( ) ;
}
2020-09-18 15:23:18 +00:00
# ifdef PCSX2_DEVBUILD
2010-12-06 21:40:36 +00:00
if ( ( oldvalue & 0x30 ) ! = ( value & 0x30 ) )
{
2020-09-18 15:23:18 +00:00
std : : string new_source ;
switch ( ( value & 0x30 ) > > 4 )
{
case 1 :
new_source = " SIF0 " ;
break ;
case 2 :
new_source = " fromSPR " ;
break ;
case 3 :
new_source = " fromIPU " ;
break ;
default :
new_source = " None " ;
break ;
}
2021-09-23 03:44:23 +00:00
//DevCon.Warning("32bit Stall Source Changed to %s", new_source.c_str());
2010-12-06 21:40:36 +00:00
}
if ( ( oldvalue & 0xC0 ) ! = ( value & 0xC0 ) )
{
2020-09-18 15:23:18 +00:00
std : : string new_dest ;
switch ( ( value & 0xC0 ) > > 6 )
{
case 1 :
new_dest = " VIF1 " ;
break ;
case 2 :
new_dest = " GIF " ;
break ;
case 3 :
new_dest = " SIF1 " ;
break ;
default :
new_dest = " None " ;
break ;
}
2021-09-23 03:44:23 +00:00
//DevCon.Warning("32bit Stall Destination Changed to %s", new_dest.c_str());
2010-12-06 21:40:36 +00:00
}
2020-09-18 15:23:18 +00:00
# endif
2010-12-06 21:40:36 +00:00
return false ;
2010-08-31 05:22:26 +00:00
}
2010-12-06 21:40:36 +00:00
//Midway are a bunch of idiots, writing to E100 (reserved) instead of E010
//Which causes a CPCOND0 to fail.
2022-05-21 18:35:19 +00:00
case ( DMAC_FAKESTAT ) :
case ( DMAC_STAT ) :
2010-12-06 21:40:36 +00:00
{
2022-05-22 20:38:02 +00:00
if ( mem = = DMAC_FAKESTAT )
2022-05-21 18:35:19 +00:00
{
HW_LOG ( " Midways own DMAC_STAT Write 32bit %x " , value ) ;
}
else HW_LOG ( " DMAC_STAT Write 32bit %x " , value ) ;
2010-08-31 05:22:26 +00:00
2010-12-06 21:40:36 +00:00
// lower 16 bits: clear on 1
// upper 16 bits: reverse on 1
psHu16 ( 0xe010 ) & = ~ ( value & 0xffff ) ;
psHu16 ( 0xe012 ) ^ = ( u16 ) ( value > > 16 ) ;
cpuTestDMACInts ( ) ;
return false ;
}
2022-05-21 18:35:19 +00:00
case ( DMAC_ENABLEW ) :
2010-08-31 05:22:26 +00:00
{
2010-12-06 21:40:36 +00:00
HW_LOG ( " DMAC_ENABLEW Write 32bit %lx " , value ) ;
oldvalue = psHu8 ( DMAC_ENABLEW + 2 ) ;
psHu32 ( DMAC_ENABLEW ) = value ;
psHu32 ( DMAC_ENABLER ) = value ;
if ( ( ( oldvalue & 0x1 ) = = 1 ) & & ( ( ( value > > 16 ) & 0x1 ) = = 0 ) )
{
if ( ! QueuedDMA . empty ( ) ) StartQueuedDMA ( ) ;
}
return false ;
2010-08-31 05:22:26 +00:00
}
2022-05-21 18:35:19 +00:00
default :
return true ;
2010-08-31 05:22:26 +00:00
}
// fall-through: use the default writeback provided by caller.
return true ;
}
2010-08-31 21:22:39 +00:00
template u32 dmacRead32 < 0x03 > ( u32 mem ) ;
2010-08-31 05:22:26 +00:00
template bool dmacWrite32 < 0x00 > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x01 > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x02 > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x03 > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x04 > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x05 > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x06 > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x07 > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x08 > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x09 > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x0a > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x0b > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x0c > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x0d > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x0e > ( u32 mem , mem32_t & value ) ;
template bool dmacWrite32 < 0x0f > ( u32 mem , mem32_t & value ) ;