PowerPC IRQ line deassertion; DMA only fires interrupts when required

PowerPC no longer clears its own IRQ line; it is now cleared by the IRQ controller when there are no more pending interrupts. Not all games clear DMA interrupts so it was necessary to tweak the 53C810 SCSI controller and the Real3D DMA interface to only fire interrupts if a certain register is correctly set. 53C810 has the documented DIEN (DMA Interrupt Enable) register; Real3D DMA seems to use the low bit of the dmaConfig register.

Also I removed the net IRQ as no games seem to actually use it.
This commit is contained in:
Matthew Daniels 2021-11-28 01:11:11 +00:00
parent 9ffce8b92a
commit e93c5d710f
7 changed files with 36 additions and 39 deletions

View file

@ -868,9 +868,15 @@ void ppc_shutdown(void)
void ppc_set_irq_line(int irqline) void ppc_set_irq_line(int irqline)
{ {
ppc.interrupt_pending |= 0x1; if (irqline)
{
ppc603_check_interrupts(); ppc.interrupt_pending |= 0x1;
ppc603_check_interrupts();
}
else
{
ppc.interrupt_pending &= ~0x1;
}
} }
UINT32 ppc_get_pc(void) UINT32 ppc_get_pc(void)

View file

@ -53,7 +53,6 @@ void ppc603_exception(int exception)
else else
ppc.npc = 0x00000000 | 0x0500; ppc.npc = 0x00000000 | 0x0500;
ppc.interrupt_pending &= ~0x1;
ppc_change_pc(ppc.npc); ppc_change_pc(ppc.npc);
} }
break; break;

View file

@ -65,6 +65,7 @@ void C53C810::SaveState(CBlockFile *SaveState)
SaveState->Write(&Ctx.regDCNTL, sizeof(Ctx.regDCNTL)); SaveState->Write(&Ctx.regDCNTL, sizeof(Ctx.regDCNTL));
SaveState->Write(&Ctx.regDMODE, sizeof(Ctx.regDMODE)); SaveState->Write(&Ctx.regDMODE, sizeof(Ctx.regDMODE));
SaveState->Write(&Ctx.regDSTAT, sizeof(Ctx.regDSTAT)); SaveState->Write(&Ctx.regDSTAT, sizeof(Ctx.regDSTAT));
SaveState->Write(&Ctx.regDIEN, sizeof(Ctx.regDIEN));
SaveState->Write(&Ctx.regISTAT, sizeof(Ctx.regISTAT)); SaveState->Write(&Ctx.regISTAT, sizeof(Ctx.regISTAT));
} }
@ -85,6 +86,7 @@ void C53C810::LoadState(CBlockFile *SaveState)
SaveState->Read(&Ctx.regDCNTL, sizeof(Ctx.regDCNTL)); SaveState->Read(&Ctx.regDCNTL, sizeof(Ctx.regDCNTL));
SaveState->Read(&Ctx.regDMODE, sizeof(Ctx.regDMODE)); SaveState->Read(&Ctx.regDMODE, sizeof(Ctx.regDMODE));
SaveState->Read(&Ctx.regDSTAT, sizeof(Ctx.regDSTAT)); SaveState->Read(&Ctx.regDSTAT, sizeof(Ctx.regDSTAT));
SaveState->Read(&Ctx.regDIEN, sizeof(Ctx.regDIEN));
SaveState->Read(&Ctx.regISTAT, sizeof(Ctx.regISTAT)); SaveState->Read(&Ctx.regISTAT, sizeof(Ctx.regISTAT));
} }
@ -105,7 +107,8 @@ static bool SCRIPTS_Int_IntFly(struct NCR53C810Context *Ctx)
Ctx->halt = true; // halt SCRIPTS execution Ctx->halt = true; // halt SCRIPTS execution
Ctx->regISTAT |= 1; // DMA interrupt pending Ctx->regISTAT |= 1; // DMA interrupt pending
Ctx->regDSTAT |= 4; // SCRIPTS interrupt instruction received Ctx->regDSTAT |= 4; // SCRIPTS interrupt instruction received
Ctx->IRQ->Assert(Ctx->scsiIRQ); if (Ctx->regDIEN & 4)
Ctx->IRQ->Assert(Ctx->scsiIRQ);
if ((Ctx->regDBC&0x100000)) // INTFLY if ((Ctx->regDBC&0x100000)) // INTFLY
return ErrorLog("53C810 INTFLY instruction not emulated!"); return ErrorLog("53C810 INTFLY instruction not emulated!");
// DSP not incremented (VF3 relies on this) // DSP not incremented (VF3 relies on this)
@ -124,7 +127,7 @@ static bool SCRIPTS_MoveMemory(struct NCR53C810Context *Ctx)
// Not implemented: illegal instruction interrupt when src and dest are not aligned the same way // Not implemented: illegal instruction interrupt when src and dest are not aligned the same way
DebugLog("53C810: Move Memory %08X -> %08X, %X\n", src, dest, numBytes); DebugLog("53C810: Move Memory %08X -> %08X, %X\n", src, dest, numBytes);
//if (dest==0x94000000)printf("53C810: Move Memory %08X -> %08X, %X\n", src, dest, numBytes); //if (dest==0x94000000)printf("53C810: Move Memory %08X -> %08X, %X\n", src, dest, numBytes);
// Perform a 32-bit copy if possible // Perform a 32-bit copy if possible
for (i = 0; i < (numBytes/4); i++) for (i = 0; i < (numBytes/4); i++)
@ -177,8 +180,11 @@ void C53C810::Run(bool singleStep)
// Issue IRQ and finish // Issue IRQ and finish
Ctx.regISTAT |= 1; // DMA interrupt pending Ctx.regISTAT |= 1; // DMA interrupt pending
Ctx.regDSTAT |= 8; // single step interrupt Ctx.regDSTAT |= 8; // single step interrupt
Ctx.IRQ->Assert(Ctx.scsiIRQ); // generate an interrupt if (Ctx.regDIEN & 8)
DebugLog("53C810: Asserted IRQ\n"); {
Ctx.IRQ->Assert(Ctx.scsiIRQ); // generate an interrupt
DebugLog("53C810: Asserted IRQ\n");
}
} }
else else
{ {
@ -317,6 +323,9 @@ void C53C810::WriteRegister(unsigned reg, UINT8 data)
case 0x38: // DMODE case 0x38: // DMODE
Ctx.regDMODE = data; Ctx.regDMODE = data;
break; break;
case 0x39: // DIEN
Ctx.regDIEN = data;
break;
case 0x3B: // DCNTL case 0x3B: // DCNTL
Ctx.regDCNTL = data; Ctx.regDCNTL = data;
if ((Ctx.regDCNTL&0x14) == 0x14) // single step if ((Ctx.regDCNTL&0x14) == 0x14) // single step
@ -396,8 +405,10 @@ UINT8 C53C810::ReadRegister(unsigned reg)
return (Ctx.regDSPS>>16)&0xFF; return (Ctx.regDSPS>>16)&0xFF;
case 0x33: // DSPS 31-24 case 0x33: // DSPS 31-24
return (Ctx.regDSPS>>24)&0xFF; return (Ctx.regDSPS>>24)&0xFF;
case 0x38: case 0x38: // DMODE
return Ctx.regDMODE; return Ctx.regDMODE;
case 0x39: // DIEN
return Ctx.regDIEN;
case 0x3B: // DCNTL case 0x3B: // DCNTL
return Ctx.regDCNTL; return Ctx.regDCNTL;
default: // get it from the register file default: // get it from the register file
@ -469,6 +480,7 @@ void C53C810::Reset(void)
Ctx.regDCNTL = 0; Ctx.regDCNTL = 0;
Ctx.regDMODE = 0; Ctx.regDMODE = 0;
Ctx.regDSTAT = 0x80; // DMA FIFO empty Ctx.regDSTAT = 0x80; // DMA FIFO empty
Ctx.regDIEN = 0;
Ctx.regISTAT = 0; Ctx.regISTAT = 0;
Ctx.halt = false; Ctx.halt = false;

View file

@ -52,6 +52,7 @@ struct NCR53C810Context
UINT8 regDCNTL; // DCNTL: DMA Control UINT8 regDCNTL; // DCNTL: DMA Control
UINT8 regDMODE; // DMODE: DMA Mode UINT8 regDMODE; // DMODE: DMA Mode
UINT8 regDSTAT; // DSTAT: DMA Status (read only) UINT8 regDSTAT; // DSTAT: DMA Status (read only)
UINT8 regDIEN; // DIEN: DMA Interrupt Enable
UINT8 regISTAT; // ISTAT: Interrupt Status UINT8 regISTAT; // ISTAT: Interrupt Status
// Operational status // Operational status

View file

@ -69,17 +69,16 @@ void CIRQ::Assert(unsigned irqBits)
{ {
irqState |= irqBits; irqState |= irqBits;
if ((irqState&irqEnable)) // low 8 bits are maskable interrupts if ((irqState&irqEnable)) // low 8 bits are maskable interrupts
//ppc_set_irq_line(0);
ppc_set_irq_line(1); ppc_set_irq_line(1);
if ((irqState&(~0xFF))) // any non-maskable interrupts pending? if ((irqState&(~0xFF))) // any non-maskable interrupts pending?
//ppc_set_irq_line(0);
ppc_set_irq_line(1); ppc_set_irq_line(1);
} }
//TO-DO: CPU needs to have deassert logic!
void CIRQ::Deassert(unsigned irqBits) void CIRQ::Deassert(unsigned irqBits)
{ {
irqState &= ~irqBits; irqState &= ~irqBits;
if (!(irqState & irqEnable) && !(irqState & (~0xFF)))
ppc_set_irq_line(0); // if no pending IRQs, deassert CPU IRQ line
} }
void CIRQ::WriteIRQEnable(UINT8 data) void CIRQ::WriteIRQEnable(UINT8 data)

View file

@ -840,13 +840,8 @@ void CModel3::WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits,
/****************************************************************************** /******************************************************************************
Model 3 System Registers Model 3 System Registers
NOTE: Proper IRQ handling requires a "deassert" function in the PowerPC core, NOTE: Different modules that generate IRQs, like the tilegen, Real3D, and
which the interpreter presently lacks. This is because different modules that SCSP, should each call IRQ.Assert() on their own.
generate IRQs, like the tilegen, Real3D, and SCSP, should each call
IRQ.Assert() on their own, which will assert the CPU IRQ line. Right now,
the CPU processes an interrupt and clears the line by itself, which means that
if multiple interrupts are asserted simultaneously, depending on the IRQ
handler code, only one may be processed. Keep an eye on this!
******************************************************************************/ ******************************************************************************/
// Set the CROM bank index (active low logic) // Set the CROM bank index (active low logic)
@ -2003,16 +1998,7 @@ void CModel3::RunFrame(void)
#ifdef NET_BOARD #ifdef NET_BOARD
if (NetBoard->IsRunning() && m_config["SimulateNet"].ValueAs<bool>()) if (NetBoard->IsRunning() && m_config["SimulateNet"].ValueAs<bool>())
{
// ppc irq network needed ? no effect, is it really active/needed ?
IRQ.Assert(0x10);
ppc_execute(200); // give PowerPC time to acknowledge IRQ
IRQ.Deassert(0x10);
ppc_execute(200); // acknowledge that IRQ was deasserted (TODO: is this really needed?)
RunNetBoardFrame(); RunNetBoardFrame();
// Hum hum, if runnetboardframe is called at 1st place or between ppc irq assert/deassert, spikout freezes just after the gate with net error
// if runnetboardframe is called after ppc irq assert/deassert, spikout works
}
#endif #endif
} }
else else
@ -2026,16 +2012,7 @@ void CModel3::RunFrame(void)
RunDriveBoardFrame(); RunDriveBoardFrame();
#ifdef NET_BOARD #ifdef NET_BOARD
if (NetBoard->IsRunning()) if (NetBoard->IsRunning())
{
// ppc irq network needed ? no effect, is it really active/needed ?
IRQ.Assert(0x10);
ppc_execute(200); // give PowerPC time to acknowledge IRQ
IRQ.Deassert(0x10);
ppc_execute(200); // acknowledge that IRQ was deasserted (TODO: is this really needed?)
RunNetBoardFrame(); RunNetBoardFrame();
// Hum hum, if runnetboardframe is called at 1st place or between ppc irq assert/deassert, spikout freezes just after the gate with net error
// if runnetboardframe is called after ppc irq assert/deassert, spikout works
}
#endif #endif
} }

View file

@ -619,8 +619,11 @@ void CReal3D::WriteDMARegister32(unsigned reg, uint32_t data)
case 0x08: // DMA length case 0x08: // DMA length
dmaLength = data; dmaLength = data;
DMACopy(); DMACopy();
dmaStatus |= 1; if (dmaConfig & 1) // only fire an IRQ if the low bit of dmaConfig is set
IRQ->Assert(dmaIRQ); {
dmaStatus |= 1;
IRQ->Assert(dmaIRQ);
}
break; break;
case 0x10: // command register case 0x10: // command register
if ((data&0x20000000)) // DMA ID command if ((data&0x20000000)) // DMA ID command