mirror of https://github.com/PCSX2/pcsx2.git
Some more changes to use dmaRegs and gifRegs in various places.
git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1736 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
c1b584ed5d
commit
4df8e8e5ce
|
@ -72,7 +72,7 @@ __forceinline void gsInterrupt()
|
|||
|
||||
if ((gif->qwc > 0) || (gspath3done == 0))
|
||||
{
|
||||
if (!(psHu32(DMAC_CTRL) & 0x1))
|
||||
if (!dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
Console::Notice("gs dma masked, re-scheduling...");
|
||||
// re-raise the int shortly in the future
|
||||
|
@ -202,13 +202,13 @@ void GIFdma()
|
|||
|
||||
gscycles = prevcycles;
|
||||
|
||||
if ((psHu32(GIF_CTRL) & 8)) // temporarily stop
|
||||
if (gifRegs->ctrl.PSE) // temporarily stop
|
||||
{
|
||||
Console::WriteLn("Gif dma temp paused?");
|
||||
return;
|
||||
}
|
||||
|
||||
if (((psHu32(DMAC_CTRL) & 0xC0) == 0x80) && (prevcycles != 0)) // STD == GIF
|
||||
if ((dmacRegs->ctrl.STD == STD_GIF) && (prevcycles != 0))
|
||||
{
|
||||
Console::WriteLn("GS Stall Control Source = %x, Drain = %x\n MADR = %x, STADR = %x", params (psHu32(0xe000) >> 4) & 0x3, (psHu32(0xe000) >> 6) & 0x3, gif->madr, psHu32(DMAC_STADR));
|
||||
|
||||
|
@ -227,14 +227,14 @@ void GIFdma()
|
|||
psHu32(GIF_STAT) |= 0x10000000; // FQC=31, hack ;) [ used to be 0xE00; // OPH=1 | APATH=3]
|
||||
|
||||
//Path2 gets priority in intermittent mode
|
||||
if (((psHu32(GIF_STAT) & GIF_STAT_P1Q) || (vif1.cmd & 0x7f) == 0x50) && (psHu32(GIF_MODE) & GIF_MODE_IMT) && (Path3progress == IMAGE_MODE))
|
||||
if ((gifRegs->stat.P1Q || (vif1.cmd & 0x7f) == 0x50) && gifRegs->mode.IMT && (Path3progress == IMAGE_MODE))
|
||||
{
|
||||
GIF_LOG("Waiting VU %x, PATH2 %x, GIFMODE %x Progress %x", psHu32(GIF_STAT) & 0x100, (vif1.cmd & 0x7f), psHu32(GIF_MODE), Path3progress);
|
||||
CPU_INT(2, 16);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vif1Regs->mskpath3 || (psHu32(GIF_MODE) & GIF_MODE_M3R))
|
||||
if (vif1Regs->mskpath3 || gifRegs->mode.M3R)
|
||||
{
|
||||
if (gif->qwc == 0)
|
||||
{
|
||||
|
@ -264,7 +264,7 @@ void GIFdma()
|
|||
if ((gif->chcr.MOD == NORMAL_MODE) || (gif->qwc > 0)) // Normal Mode
|
||||
{
|
||||
|
||||
if (((psHu32(DMAC_CTRL) & 0xC0) == 0x80) && (gif->chcr.MOD == NORMAL_MODE))
|
||||
if ((dmacRegs->ctrl.STD == STD_GIF) && (gif->chcr.MOD == NORMAL_MODE))
|
||||
{
|
||||
Console::WriteLn("DMA Stall Control on GIF normal");
|
||||
}
|
||||
|
@ -280,7 +280,7 @@ void GIFdma()
|
|||
if (!ReadTag(ptag, id)) return;
|
||||
GIF_LOG("gifdmaChain %8.8x_%8.8x size=%d, id=%d, addr=%lx", ptag[1], ptag[0], gif->qwc, id, gif->madr);
|
||||
|
||||
if ((psHu32(DMAC_CTRL) & 0xC0) == 0x80) // STD == GIF
|
||||
if (dmacRegs->ctrl.STD == STD_GIF)
|
||||
{
|
||||
// there are still bugs, need to also check if gif->madr +16*qwc >= stadr, if not, stall
|
||||
if (!gspath3done && ((gif->madr + (gif->qwc * 16)) > psHu32(DMAC_STADR)) && (id == 4))
|
||||
|
@ -333,8 +333,8 @@ void dmaGIF()
|
|||
psHu32(GIF_STAT) |= GIF_STAT_P3Q;
|
||||
psHu32(GIF_STAT) |= 0x10000000; // FQC=31, hack ;) [used to be 0xE00; // OPH=1 | APATH=3]
|
||||
clearFIFOstuff(true);
|
||||
|
||||
if ((psHu32(DMAC_CTRL) & 0xC) == 0xC ) // GIF MFIFO
|
||||
|
||||
if (dmacRegs->ctrl.MFD == MFD_GIF) // GIF MFIFO
|
||||
{
|
||||
//Console::WriteLn("GIF MFIFO");
|
||||
gifMFIFOInterrupt();
|
||||
|
@ -546,7 +546,7 @@ void gifMFIFOInterrupt()
|
|||
return;
|
||||
}
|
||||
|
||||
if (((psHu32(GIF_STAT) & GIF_STAT_P1Q) || (vif1.cmd & 0x7f) == 0x50) && (psHu32(GIF_MODE) & GIF_MODE_IMT) && Path3progress == IMAGE_MODE) //Path2 gets priority in intermittent mode
|
||||
if ((gifRegs->stat.P1Q || (vif1.cmd & 0x7f) == 0x50) && gifRegs->mode.IMT && Path3progress == IMAGE_MODE) //Path2 gets priority in intermittent mode
|
||||
{
|
||||
//GIF_LOG("Waiting VU %x, PATH2 %x, GIFMODE %x Progress %x", psHu32(GIF_STAT) & 0x100, (vif1.cmd & 0x7f), psHu32(GIF_MODE), Path3progress);
|
||||
CPU_INT(11,mfifocycles);
|
||||
|
@ -559,7 +559,7 @@ void gifMFIFOInterrupt()
|
|||
{
|
||||
//Console::WriteLn("Empty");
|
||||
gifstate |= GIF_STATE_EMPTY;
|
||||
psHu32(GIF_STAT) &= ~GIF_STAT_IMT; // OPH=0 | APATH=0
|
||||
gifRegs->stat.IMT = 0; // OPH=0 | APATH=0
|
||||
hwDmacIrq(DMAC_MFIFO_EMPTY);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ __forceinline void dmacInterrupt()
|
|||
if( ((psHu16(0xe012) & psHu16(0xe010)) == 0 ) &&
|
||||
( psHu16(0xe010) & 0x8000) == 0 ) return;
|
||||
|
||||
if((psHu32(DMAC_CTRL) & 0x1) == 0) return;
|
||||
if (!(dmacRegs->ctrl.DMAE)) return;
|
||||
|
||||
HW_LOG("dmacInterrupt %x", (psHu16(0xe012) & psHu16(0xe010) ||
|
||||
psHu16(0xe010) & 0x8000));
|
||||
|
|
53
pcsx2/Hw.h
53
pcsx2/Hw.h
|
@ -62,6 +62,15 @@ union tDMA_CHCR {
|
|||
u32 _u32;
|
||||
};
|
||||
|
||||
union tDMA_SADR {
|
||||
struct {
|
||||
u32 ADDR : 14;
|
||||
u32 reserved2 : 18;
|
||||
};
|
||||
u32 _u32;
|
||||
};
|
||||
|
||||
|
||||
struct DMACh {
|
||||
tDMA_CHCR chcr;
|
||||
u32 null0[3];
|
||||
|
@ -448,30 +457,12 @@ union tDMAC_CTRL {
|
|||
|
||||
union tDMAC_STAT {
|
||||
struct {
|
||||
u32 CIS0 : 1;
|
||||
u32 CIS1 : 1;
|
||||
u32 CIS2 : 1;
|
||||
u32 CIS3 : 1;
|
||||
u32 CIS4 : 1;
|
||||
u32 CIS5 : 1;
|
||||
u32 CIS6 : 1;
|
||||
u32 CIS7 : 1;
|
||||
u32 CIS8 : 1;
|
||||
u32 CIS9 : 1;
|
||||
u32 CIS : 10;
|
||||
u32 reserved1 : 3;
|
||||
u32 SIS : 1;
|
||||
u32 MEIS : 1;
|
||||
u32 BEIS : 1;
|
||||
u32 CIM0 : 1;
|
||||
u32 CIM1 : 1;
|
||||
u32 CIM2 : 1;
|
||||
u32 CIM3 : 1;
|
||||
u32 CIM4 : 1;
|
||||
u32 CIM5 : 1;
|
||||
u32 CIM6 : 1;
|
||||
u32 CIM7 : 1;
|
||||
u32 CIM8 : 1;
|
||||
u32 CIM9 : 1;
|
||||
u32 CIM : 10;
|
||||
u32 reserved2 : 3;
|
||||
u32 SIM : 1;
|
||||
u32 MEIM : 1;
|
||||
|
@ -482,27 +473,9 @@ union tDMAC_STAT {
|
|||
|
||||
union tDMAC_PCR {
|
||||
struct {
|
||||
u32 CPC0 : 1;
|
||||
u32 CPC1 : 1;
|
||||
u32 CPC2 : 1;
|
||||
u32 CPC3 : 1;
|
||||
u32 CPC4 : 1;
|
||||
u32 CPC5 : 1;
|
||||
u32 CPC6 : 1;
|
||||
u32 CPC7 : 1;
|
||||
u32 CPC8 : 1;
|
||||
u32 CPC9 : 1;
|
||||
u32 CPC : 10;
|
||||
u32 reserved1 : 6;
|
||||
u32 CDE0 : 1;
|
||||
u32 CDE1 : 1;
|
||||
u32 CDE2 : 1;
|
||||
u32 CDE3 : 1;
|
||||
u32 CDE4 : 1;
|
||||
u32 CDE5 : 1;
|
||||
u32 CDE6 : 1;
|
||||
u32 CDE7 : 1;
|
||||
u32 CDE8 : 1;
|
||||
u32 CDE9 : 1;
|
||||
u32 CDE : 10;
|
||||
u32 reserved2 : 5;
|
||||
u32 PCE : 1;
|
||||
};
|
||||
|
|
|
@ -43,8 +43,8 @@ static __forceinline void DmaExec8( void (*func)(), u32 mem, u8 value )
|
|||
{
|
||||
u32 qwcRegister = (mem | 0x20) & ~0x1; //Need to remove the lower bit else we end up clearing TADR
|
||||
|
||||
//Its invalid for the hardware to write a DMA while it is active, not without Suspending the DMAC
|
||||
if ((value & 0x1) && ((psHu8(mem) & 0x1) == 0x1) && ((psHu32(DMAC_CTRL) & 0x1) == 1))
|
||||
//It's invalid for the hardware to write a DMA while it is active, not without Suspending the DMAC
|
||||
if ((value & 0x1) && ((psHu8(mem) & 0x1) == 0x1) && dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DMA_LOG( "DMAExec8 Attempt to run DMA while one is already active mem = %x", mem );
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ static __forceinline void DmaExec8( void (*func)(), u32 mem, u8 value )
|
|||
}
|
||||
|
||||
psHu8(mem) = (u8)value;
|
||||
if ((psHu8(mem) & 0x1) && (psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((psHu8(mem) & 0x1) && dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
/*Console::WriteLn("Running DMA 8 %x", params psHu32(mem & ~0x1));*/
|
||||
func();
|
||||
|
@ -70,8 +70,8 @@ static __forceinline void DmaExec16( void (*func)(), u32 mem, u16 value )
|
|||
{
|
||||
u32 qwcRegister = mem | 0x20;
|
||||
|
||||
//Its invalid for the hardware to write a DMA while it is active, not without Suspending the DMAC
|
||||
if ((value & 0x100) && ((psHu32(mem) & 0x100) == 0x100) && ((psHu32(DMAC_CTRL) & 0x1) == 1))
|
||||
//It's invalid for the hardware to write a DMA while it is active, not without Suspending the DMAC
|
||||
if ((value & 0x100) && ((psHu32(mem) & 0x100) == 0x100) && dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DMA_LOG( "DMAExec16 Attempt to run DMA while one is already active mem = %x", mem);
|
||||
}
|
||||
|
@ -97,8 +97,8 @@ static void DmaExec( void (*func)(), u32 mem, u32 value )
|
|||
{
|
||||
u32 qwcRegister = mem | 0x20;
|
||||
|
||||
//Its invalid for the hardware to write a DMA while it is active, not without Suspending the DMAC
|
||||
if ((value & 0x100) && ((psHu32(mem) & 0x100) == 0x100) && ((psHu32(DMAC_CTRL) & 0x1) == 1))
|
||||
//It's invalid for the hardware to write a DMA while it is active, not without Suspending the DMAC
|
||||
if ((value & 0x100) && ((psHu32(mem) & 0x100) == 0x100) && dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DMA_LOG( "DMAExec32 Attempt to run DMA while one is already active mem = %x", mem );
|
||||
|
||||
|
@ -122,7 +122,7 @@ static void DmaExec( void (*func)(), u32 mem, u32 value )
|
|||
else /* Else (including Normal mode etc) write whatever the hardware sends*/
|
||||
psHu32(mem) = (u32)value;
|
||||
|
||||
if ((psHu32(mem) & 0x100) && (psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((psHu32(mem) & 0x100) && dmacRegs->ctrl.DMAE)
|
||||
func();
|
||||
}
|
||||
|
||||
|
@ -199,7 +199,7 @@ void hwWrite8(u32 mem, u8 value)
|
|||
// break;
|
||||
case D0_CHCR + 1: // dma0 - vif0
|
||||
DMA_LOG("VIF0dma EXECUTE, value=0x%x", value);
|
||||
if ((value & 0x1) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x1) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("8 bit VIF0 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x1;
|
||||
|
@ -209,7 +209,7 @@ void hwWrite8(u32 mem, u8 value)
|
|||
|
||||
case D1_CHCR + 1: // dma1 - vif1
|
||||
DMA_LOG("VIF1dma EXECUTE, value=0x%x", value);
|
||||
if ((value & 0x1) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x1) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("8 bit VIF1 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x2;
|
||||
|
@ -220,7 +220,7 @@ void hwWrite8(u32 mem, u8 value)
|
|||
|
||||
case D2_CHCR + 1: // dma2 - gif
|
||||
DMA_LOG("GSdma EXECUTE, value=0x%x", value);
|
||||
if ((value & 0x1) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x1) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("8 bit GIF DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x4;
|
||||
|
@ -230,7 +230,7 @@ void hwWrite8(u32 mem, u8 value)
|
|||
|
||||
case D3_CHCR + 1: // dma3 - fromIPU
|
||||
DMA_LOG("IPU0dma EXECUTE, value=0x%x", value);
|
||||
if ((value & 0x1) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x1) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("8 bit IPU0 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x8;
|
||||
|
@ -240,7 +240,7 @@ void hwWrite8(u32 mem, u8 value)
|
|||
|
||||
case D4_CHCR + 1: // dma4 - toIPU
|
||||
DMA_LOG("IPU1dma EXECUTE, value=0x%x", value);
|
||||
if ((value & 0x1) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x1) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("8 bit IPU1 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x10;
|
||||
|
@ -251,7 +251,7 @@ void hwWrite8(u32 mem, u8 value)
|
|||
case D5_CHCR + 1: // dma5 - sif0
|
||||
DMA_LOG("SIF0dma EXECUTE, value=0x%x", value);
|
||||
// if (value == 0) psxSu32(0x30) = 0x40000;
|
||||
if ((value & 0x1) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x1) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("8 bit SIF0 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x20;
|
||||
|
@ -261,7 +261,7 @@ void hwWrite8(u32 mem, u8 value)
|
|||
|
||||
case D6_CHCR + 1: // dma6 - sif1
|
||||
DMA_LOG("SIF1dma EXECUTE, value=0x%x", value);
|
||||
if ((value & 0x1) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x1) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("8 bit SIF1 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x40;
|
||||
|
@ -271,7 +271,7 @@ void hwWrite8(u32 mem, u8 value)
|
|||
|
||||
case D7_CHCR + 1: // dma7 - sif2
|
||||
DMA_LOG("SIF2dma EXECUTE, value=0x%x", value);
|
||||
if ((value & 0x1) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x1) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("8 bit SIF2 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x80;
|
||||
|
@ -281,7 +281,7 @@ void hwWrite8(u32 mem, u8 value)
|
|||
|
||||
case D8_CHCR + 1: // dma8 - fromSPR
|
||||
DMA_LOG("fromSPRdma8 EXECUTE, value=0x%x", value);
|
||||
if ((value & 0x1) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x1) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("8 bit SPR0 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x100;
|
||||
|
@ -291,7 +291,7 @@ void hwWrite8(u32 mem, u8 value)
|
|||
|
||||
case SPR1_CHCR + 1: // dma9 - toSPR
|
||||
DMA_LOG("toSPRdma8 EXECUTE, value=0x%x", value);
|
||||
if ((value & 0x1) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x1) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("8 bit SPR1 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x200;
|
||||
|
@ -376,7 +376,7 @@ __forceinline void hwWrite16(u32 mem, u16 value)
|
|||
|
||||
case D0_CHCR: // dma0 - vif0
|
||||
DMA_LOG("VIF0dma %lx", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("16 bit VIF0 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x1;
|
||||
|
@ -386,7 +386,7 @@ __forceinline void hwWrite16(u32 mem, u16 value)
|
|||
|
||||
case D1_CHCR: // dma1 - vif1 - chcr
|
||||
DMA_LOG("VIF1dma CHCR %lx", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("16 bit VIF1 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x2;
|
||||
|
@ -430,7 +430,7 @@ __forceinline void hwWrite16(u32 mem, u16 value)
|
|||
|
||||
case D2_CHCR: // dma2 - gif
|
||||
DMA_LOG("0x%8.8x hwWrite32: GSdma %lx", cpuRegs.cycle, value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("16 bit GIF DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x4;
|
||||
|
@ -472,7 +472,7 @@ __forceinline void hwWrite16(u32 mem, u16 value)
|
|||
|
||||
case D3_CHCR: // dma3 - fromIPU
|
||||
DMA_LOG("IPU0dma %lx", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("16 bit IPU0 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x8;
|
||||
|
@ -504,7 +504,7 @@ __forceinline void hwWrite16(u32 mem, u16 value)
|
|||
|
||||
case D4_CHCR: // dma4 - toIPU
|
||||
DMA_LOG("IPU1dma %lx", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("16 bit IPU1 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x10;
|
||||
|
@ -536,7 +536,7 @@ __forceinline void hwWrite16(u32 mem, u16 value)
|
|||
case D5_CHCR: // dma5 - sif0
|
||||
DMA_LOG("SIF0dma %lx", value);
|
||||
// if (value == 0) psxSu32(0x30) = 0x40000;
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("16 bit SIF0 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x20;
|
||||
|
@ -550,7 +550,7 @@ __forceinline void hwWrite16(u32 mem, u16 value)
|
|||
|
||||
case D6_CHCR: // dma6 - sif1
|
||||
DMA_LOG("SIF1dma %lx", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("16 bit SIF1 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x40;
|
||||
|
@ -582,7 +582,7 @@ __forceinline void hwWrite16(u32 mem, u16 value)
|
|||
|
||||
case D7_CHCR: // dma7 - sif2
|
||||
DMA_LOG("SIF2dma %lx", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("16 bit SIF2 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x80;
|
||||
|
@ -596,7 +596,7 @@ __forceinline void hwWrite16(u32 mem, u16 value)
|
|||
|
||||
case D8_CHCR: // dma8 - fromSPR
|
||||
DMA_LOG("fromSPRdma %lx", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("16 bit SPR0 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x100;
|
||||
|
@ -606,7 +606,7 @@ __forceinline void hwWrite16(u32 mem, u16 value)
|
|||
|
||||
case SPR1_CHCR: // dma9 - toSPR
|
||||
DMA_LOG("toSPRdma %lx", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("16 bit SPR1 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x200;
|
||||
|
@ -763,7 +763,7 @@ void __fastcall hwWrite32_page_0B( u32 mem, u32 value )
|
|||
{
|
||||
case D3_CHCR: // dma3 - fromIPU
|
||||
DMA_LOG("IPU0dma EXECUTE, value=0x%x\n", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("32 bit IPU0 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x8;
|
||||
|
@ -780,7 +780,7 @@ void __fastcall hwWrite32_page_0B( u32 mem, u32 value )
|
|||
|
||||
case D4_CHCR: // dma4 - toIPU
|
||||
DMA_LOG("IPU1dma EXECUTE, value=0x%x\n", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("32 bit IPU1 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x10;
|
||||
|
@ -931,7 +931,7 @@ void __fastcall hwWrite32_generic( u32 mem, u32 value )
|
|||
case D0_CHCR: // dma0 - vif0
|
||||
DMA_LOG("VIF0dma EXECUTE, value=0x%x", value);
|
||||
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("32 bit VIF0 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x1;
|
||||
|
@ -944,7 +944,7 @@ void __fastcall hwWrite32_generic( u32 mem, u32 value )
|
|||
case D1_CHCR: // dma1 - vif1 - chcr
|
||||
DMA_LOG("VIF1dma EXECUTE, value=0x%x", value);
|
||||
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("32 bit VIF1 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x2;
|
||||
|
@ -968,7 +968,7 @@ void __fastcall hwWrite32_generic( u32 mem, u32 value )
|
|||
//------------------------------------------------------------------
|
||||
case D2_CHCR: // dma2 - gif
|
||||
DMA_LOG("GIFdma EXECUTE, value=0x%x", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("32 bit GIF DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x4;
|
||||
|
@ -987,7 +987,7 @@ void __fastcall hwWrite32_generic( u32 mem, u32 value )
|
|||
case D5_CHCR: // dma5 - sif0
|
||||
DMA_LOG("SIF0dma EXECUTE, value=0x%x", value);
|
||||
//if (value == 0) psxSu32(0x30) = 0x40000;
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("32 bit SIF0 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x20;
|
||||
|
@ -997,7 +997,7 @@ void __fastcall hwWrite32_generic( u32 mem, u32 value )
|
|||
//------------------------------------------------------------------
|
||||
case D6_CHCR: // dma6 - sif1
|
||||
DMA_LOG("SIF1dma EXECUTE, value=0x%x", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("32 bit SIF1 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x40;
|
||||
|
@ -1012,7 +1012,7 @@ void __fastcall hwWrite32_generic( u32 mem, u32 value )
|
|||
//------------------------------------------------------------------
|
||||
case D7_CHCR: // dma7 - sif2
|
||||
DMA_LOG("SIF2dma EXECUTE, value=0x%x", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("32 bit SIF2 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x80;
|
||||
|
@ -1022,7 +1022,7 @@ void __fastcall hwWrite32_generic( u32 mem, u32 value )
|
|||
//------------------------------------------------------------------
|
||||
case D8_CHCR: // dma8 - fromSPR
|
||||
DMA_LOG("SPR0dma EXECUTE (fromSPR), value=0x%x", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("32 bit SPR0 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x100;
|
||||
|
@ -1032,7 +1032,7 @@ void __fastcall hwWrite32_generic( u32 mem, u32 value )
|
|||
//------------------------------------------------------------------
|
||||
case SPR1_CHCR: // dma9 - toSPR
|
||||
DMA_LOG("SPR1dma EXECUTE (toSPR), value=0x%x", value);
|
||||
if ((value & 0x100) && !(psHu32(DMAC_CTRL) & 0x1))
|
||||
if ((value & 0x100) && !dmacRegs->ctrl.DMAE)
|
||||
{
|
||||
DevCon::Notice("32 bit SPR1 DMA Start while DMAC Disabled\n");
|
||||
QueuedDMA |= 0x200;
|
||||
|
@ -1077,9 +1077,9 @@ void __fastcall hwWrite64_page_03( u32 mem, const mem64_t* srcval )
|
|||
else
|
||||
{
|
||||
if( value & 8 )
|
||||
psHu32(GIF_STAT) |= GIF_STAT_PSE;
|
||||
gifRegs->stat.PSE = 1;
|
||||
else
|
||||
psHu32(GIF_STAT) &= ~GIF_STAT_PSE;
|
||||
gifRegs->stat.PSE = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
121
pcsx2/SPR.cpp
121
pcsx2/SPR.cpp
|
@ -61,30 +61,32 @@ int _SPR0chain()
|
|||
pMem = (u32*)dmaGetAddr(spr0->madr);
|
||||
if (pMem == NULL) return -1;
|
||||
|
||||
if ((psHu32(DMAC_CTRL) & 0xC) >= 0x8) // 0x8 VIF1 MFIFO, 0xC GIF MFIFO
|
||||
switch (dmacRegs->ctrl.MFD)
|
||||
{
|
||||
if ((spr0->madr & ~psHu32(DMAC_RBSR)) != psHu32(DMAC_RBOR))
|
||||
Console::WriteLn("SPR MFIFO Write outside MFIFO area");
|
||||
else
|
||||
mfifotransferred += spr0->qwc;
|
||||
|
||||
hwMFIFOWrite(spr0->madr, (u8*)&PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc << 4);
|
||||
spr0->madr += spr0->qwc << 4;
|
||||
spr0->madr = psHu32(DMAC_RBOR) + (spr0->madr & psHu32(DMAC_RBSR));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy_fast((u8*)pMem, &PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc << 4);
|
||||
|
||||
// clear VU mem also!
|
||||
TestClearVUs(spr0->madr, spr0->qwc << 2); // Wtf is going on here? AFAIK, only VIF should affect VU micromem (cottonvibes)
|
||||
|
||||
spr0->madr += spr0->qwc << 4;
|
||||
case MFD_VIF1:
|
||||
case MFD_GIF:
|
||||
if ((spr0->madr & ~psHu32(DMAC_RBSR)) != psHu32(DMAC_RBOR))
|
||||
Console::WriteLn("SPR MFIFO Write outside MFIFO area");
|
||||
else
|
||||
mfifotransferred += spr0->qwc;
|
||||
|
||||
hwMFIFOWrite(spr0->madr, (u8*)&PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc << 4);
|
||||
spr0->madr += spr0->qwc << 4;
|
||||
spr0->madr = psHu32(DMAC_RBOR) + (spr0->madr & psHu32(DMAC_RBSR));
|
||||
break;
|
||||
|
||||
case NO_MFD:
|
||||
case MFD_RESERVED:
|
||||
memcpy_fast((u8*)pMem, &PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc << 4);
|
||||
|
||||
// clear VU mem also!
|
||||
TestClearVUs(spr0->madr, spr0->qwc << 2); // Wtf is going on here? AFAIK, only VIF should affect VU micromem (cottonvibes)
|
||||
spr0->madr += spr0->qwc << 4;
|
||||
break;
|
||||
}
|
||||
|
||||
spr0->sadr += spr0->qwc << 4;
|
||||
|
||||
|
||||
return (spr0->qwc) * BIAS; // bus is 1/2 the ee speed
|
||||
}
|
||||
|
||||
|
@ -98,8 +100,8 @@ __forceinline void SPR0chain()
|
|||
void _SPR0interleave()
|
||||
{
|
||||
int qwc = spr0->qwc;
|
||||
int sqwc = psHu32(DMAC_SQWC) & 0xff;
|
||||
int tqwc = (psHu32(DMAC_SQWC) >> 16) & 0xff;
|
||||
int sqwc = dmacRegs->sqwc.SQWC;
|
||||
int tqwc = dmacRegs->sqwc.TQWC;
|
||||
u32 *pMem;
|
||||
|
||||
if (tqwc == 0) tqwc = qwc;
|
||||
|
@ -112,18 +114,22 @@ void _SPR0interleave()
|
|||
spr0->qwc = std::min(tqwc, qwc);
|
||||
qwc -= spr0->qwc;
|
||||
pMem = (u32*)dmaGetAddr(spr0->madr);
|
||||
if ((((psHu32(DMAC_CTRL) & 0xC) == 0xC) || // GIF MFIFO
|
||||
(psHu32(DMAC_CTRL) & 0xC) == 0x8)) // VIF1 MFIFO
|
||||
{
|
||||
hwMFIFOWrite(spr0->madr, (u8*)&PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc << 4);
|
||||
mfifotransferred += spr0->qwc;
|
||||
}
|
||||
else
|
||||
{
|
||||
// clear VU mem also!
|
||||
TestClearVUs(spr0->madr, spr0->qwc << 2);
|
||||
memcpy_fast((u8*)pMem, &PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc << 4);
|
||||
}
|
||||
|
||||
switch (dmacRegs->ctrl.MFD)
|
||||
{
|
||||
case MFD_VIF1:
|
||||
case MFD_GIF:
|
||||
hwMFIFOWrite(spr0->madr, (u8*)&PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc << 4);
|
||||
mfifotransferred += spr0->qwc;
|
||||
break;
|
||||
|
||||
case NO_MFD:
|
||||
case MFD_RESERVED:
|
||||
// clear VU mem also!
|
||||
TestClearVUs(spr0->madr, spr0->qwc << 2);
|
||||
memcpy_fast((u8*)pMem, &PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc << 4);
|
||||
break;
|
||||
}
|
||||
spr0->sadr += spr0->qwc * 16;
|
||||
spr0->madr += (sqwc + spr0->qwc) * 16;
|
||||
}
|
||||
|
@ -134,7 +140,7 @@ void _SPR0interleave()
|
|||
|
||||
static __forceinline void _dmaSPR0()
|
||||
{
|
||||
if ((psHu32(DMAC_CTRL) & 0x30) == 0x20) // STS == fromSPR
|
||||
if (dmacRegs->ctrl.STS == STS_fromSPR) // STS == fromSPR
|
||||
{
|
||||
Console::WriteLn("SPR0 stall %d", params(psHu32(DMAC_CTRL) >> 6)&3);
|
||||
}
|
||||
|
@ -172,7 +178,7 @@ static __forceinline void _dmaSPR0()
|
|||
SPR_LOG("spr0 dmaChain %8.8x_%8.8x size=%d, id=%d, addr=%lx spr=%lx",
|
||||
ptag[1], ptag[0], spr0->qwc, id, spr0->madr, spr0->sadr);
|
||||
|
||||
if ((psHu32(DMAC_CTRL) & 0x30) == 0x20) // STS == fromSPR
|
||||
if (dmacRegs->ctrl.STS == STS_fromSPR) // STS == fromSPR
|
||||
{
|
||||
Console::WriteLn("SPR stall control");
|
||||
}
|
||||
|
@ -225,23 +231,30 @@ void SPRFROMinterrupt()
|
|||
|
||||
if(mfifotransferred != 0)
|
||||
{
|
||||
if ((psHu32(DMAC_CTRL) & 0xC) == 0xC) // GIF MFIFO
|
||||
switch (dmacRegs->ctrl.MFD)
|
||||
{
|
||||
if ((spr0->madr & ~psHu32(DMAC_RBSR)) != psHu32(DMAC_RBOR)) Console::WriteLn("GIF MFIFO Write outside MFIFO area");
|
||||
spr0->madr = psHu32(DMAC_RBOR) + (spr0->madr & psHu32(DMAC_RBSR));
|
||||
//Console::WriteLn("mfifoGIFtransfer %x madr %x, tadr %x", params gif->chcr._u32, gif->madr, gif->tadr);
|
||||
mfifoGIFtransfer(mfifotransferred);
|
||||
mfifotransferred = 0;
|
||||
if (gif->chcr.STR) return;
|
||||
}
|
||||
else if ((psHu32(DMAC_CTRL) & 0xC) == 0x8) // VIF1 MFIFO
|
||||
{
|
||||
if ((spr0->madr & ~psHu32(DMAC_RBSR)) != psHu32(DMAC_RBOR)) Console::WriteLn("VIF MFIFO Write outside MFIFO area");
|
||||
spr0->madr = psHu32(DMAC_RBOR) + (spr0->madr & psHu32(DMAC_RBSR));
|
||||
//Console::WriteLn("mfifoVIF1transfer %x madr %x, tadr %x", params vif1ch->chcr._u32, vif1ch->madr, vif1ch->tadr);
|
||||
mfifoVIF1transfer(mfifotransferred);
|
||||
mfifotransferred = 0;
|
||||
if (vif1ch->chcr.STR) return;
|
||||
case MFD_GIF:
|
||||
{
|
||||
if ((spr0->madr & ~psHu32(DMAC_RBSR)) != psHu32(DMAC_RBOR)) Console::WriteLn("GIF MFIFO Write outside MFIFO area");
|
||||
spr0->madr = psHu32(DMAC_RBOR) + (spr0->madr & psHu32(DMAC_RBSR));
|
||||
//Console::WriteLn("mfifoGIFtransfer %x madr %x, tadr %x", params gif->chcr._u32, gif->madr, gif->tadr);
|
||||
mfifoGIFtransfer(mfifotransferred);
|
||||
mfifotransferred = 0;
|
||||
if (gif->chcr.STR) return;
|
||||
break;
|
||||
}
|
||||
case MFD_VIF1:
|
||||
{
|
||||
if ((spr0->madr & ~psHu32(DMAC_RBSR)) != psHu32(DMAC_RBOR)) Console::WriteLn("VIF MFIFO Write outside MFIFO area");
|
||||
spr0->madr = psHu32(DMAC_RBOR) + (spr0->madr & psHu32(DMAC_RBSR));
|
||||
//Console::WriteLn("mfifoVIF1transfer %x madr %x, tadr %x", params vif1ch->chcr._u32, vif1ch->madr, vif1ch->tadr);
|
||||
mfifoVIF1transfer(mfifotransferred);
|
||||
mfifotransferred = 0;
|
||||
if (vif1ch->chcr.STR) return;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (spr0finished == 0) return;
|
||||
|
@ -300,8 +313,8 @@ __forceinline void SPR1chain()
|
|||
void _SPR1interleave()
|
||||
{
|
||||
int qwc = spr1->qwc;
|
||||
int sqwc = psHu32(DMAC_SQWC) & 0xff;
|
||||
int tqwc = (psHu32(DMAC_SQWC) >> 16) & 0xff;
|
||||
int sqwc = dmacRegs->sqwc.SQWC;
|
||||
int tqwc = dmacRegs->sqwc.TQWC;
|
||||
u32 *pMem;
|
||||
|
||||
if (tqwc == 0) tqwc = qwc;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "Vif.h"
|
||||
#include "VUmicro.h"
|
||||
#include "GS.h"
|
||||
#include "Gif.h"
|
||||
#include "VifDma.h"
|
||||
#include "Tags.h"
|
||||
|
||||
|
@ -1447,7 +1448,7 @@ void vif0Interrupt()
|
|||
if ((vif0ch->chcr.MOD == CHAIN_MODE) && (!vif0.done) && (!vif0.vifstalled))
|
||||
{
|
||||
|
||||
if (!(psHu32(DMAC_CTRL) & 0x1))
|
||||
if (!(dmacRegs->ctrl.DMAE))
|
||||
{
|
||||
Console::WriteLn("vif0 dma masked");
|
||||
return;
|
||||
|
@ -2344,7 +2345,7 @@ void vif1TransferFromMemory()
|
|||
if (pMem == NULL) //Is vif0ptag empty?
|
||||
{
|
||||
Console::WriteLn("Vif1 Tag BUSERR");
|
||||
psHu32(DMAC_STAT) |= DMAC_STAT_BEIS; //If yes, set BEIS (BUSERR) in DMAC_STAT register
|
||||
dmacRegs->stat.BEIS = 1; //If yes, set BEIS (BUSERR) in DMAC_STAT register
|
||||
vif1.done = true;
|
||||
vif1Regs->stat &= ~VIF1_STAT_FQC;
|
||||
vif1ch->qwc = 0;
|
||||
|
@ -2461,7 +2462,7 @@ __forceinline void vif1SetupTransfer()
|
|||
VIF_LOG("VIF1 Tag %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx\n",
|
||||
vif1ptag[1], vif1ptag[0], vif1ch->qwc, id, vif1ch->madr, vif1ch->tadr);
|
||||
|
||||
if (!vif1.done && ((psHu32(DMAC_CTRL) & 0xC0) == 0x40) && (id == 4)) // STD == VIF1
|
||||
if (!vif1.done && ((dmacRegs->ctrl.STD == STD_VIF1) && (id == 4))) // STD == VIF1
|
||||
{
|
||||
// there are still bugs, need to also check if gif->madr +16*qwc >= stadr, if not, stall
|
||||
if ((vif1ch->madr + vif1ch->qwc * 16) >= psHu32(DMAC_STADR))
|
||||
|
@ -2557,7 +2558,7 @@ __forceinline void vif1Interrupt()
|
|||
if (!vif1.done)
|
||||
{
|
||||
|
||||
if (!(psHu32(DMAC_CTRL) & 0x1))
|
||||
if (!(dmacRegs->ctrl.DMAE))
|
||||
{
|
||||
Console::WriteLn("vif1 dma masked");
|
||||
return;
|
||||
|
@ -2601,7 +2602,7 @@ void dmaVIF1()
|
|||
g_vifCycles = 0;
|
||||
vif1.inprogress = 0;
|
||||
|
||||
if (((psHu32(DMAC_CTRL) & 0xC) == 0x8)) // VIF MFIFO
|
||||
if (dmacRegs->ctrl.MFD == MFD_VIF1) // VIF MFIFO
|
||||
{
|
||||
//Console::WriteLn("VIFMFIFO\n");
|
||||
// Test changed because the Final Fantasy 12 opening somehow has the tag in *Undefined* mode, which is not in the documentation that I saw.
|
||||
|
@ -2611,7 +2612,7 @@ void dmaVIF1()
|
|||
}
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
if ((psHu32(DMAC_CTRL) & 0xC0) == 0x40) // STD == VIF1
|
||||
if (dmacRegs->ctrl.STD == STD_VIF1)
|
||||
{
|
||||
//DevCon::WriteLn("VIF Stall Control Source = %x, Drain = %x", params (psHu32(0xe000) >> 4) & 0x3, (psHu32(0xe000) >> 6) & 0x3);
|
||||
}
|
||||
|
@ -2620,7 +2621,7 @@ void dmaVIF1()
|
|||
if ((vif1ch->chcr.MOD == NORMAL_MODE) || vif1ch->qwc > 0) // Normal Mode
|
||||
{
|
||||
|
||||
if ((psHu32(DMAC_CTRL) & 0xC0) == 0x40)
|
||||
if (dmacRegs->ctrl.STD == STD_VIF1)
|
||||
Console::WriteLn("DMA Stall Control on VIF1 normal");
|
||||
|
||||
if (vif1ch->chcr.DIR) // to Memory
|
||||
|
@ -2671,7 +2672,7 @@ void vif1Write32(u32 mem, u32 value)
|
|||
if(vif1Regs->mskpath3)
|
||||
{
|
||||
vif1Regs->mskpath3 = 0;
|
||||
psHu32(GIF_STAT) &= ~GIF_STAT_IMT;
|
||||
gifRegs->stat.IMT = 0;
|
||||
if (gif->chcr.STR) CPU_INT(2, 4);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue