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)
{
ppc.interrupt_pending |= 0x1;
ppc603_check_interrupts();
if (irqline)
{
ppc.interrupt_pending |= 0x1;
ppc603_check_interrupts();
}
else
{
ppc.interrupt_pending &= ~0x1;
}
}
UINT32 ppc_get_pc(void)

View file

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

View file

@ -65,6 +65,7 @@ void C53C810::SaveState(CBlockFile *SaveState)
SaveState->Write(&Ctx.regDCNTL, sizeof(Ctx.regDCNTL));
SaveState->Write(&Ctx.regDMODE, sizeof(Ctx.regDMODE));
SaveState->Write(&Ctx.regDSTAT, sizeof(Ctx.regDSTAT));
SaveState->Write(&Ctx.regDIEN, sizeof(Ctx.regDIEN));
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.regDMODE, sizeof(Ctx.regDMODE));
SaveState->Read(&Ctx.regDSTAT, sizeof(Ctx.regDSTAT));
SaveState->Read(&Ctx.regDIEN, sizeof(Ctx.regDIEN));
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->regISTAT |= 1; // DMA interrupt pending
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
return ErrorLog("53C810 INTFLY instruction not emulated!");
// 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
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
for (i = 0; i < (numBytes/4); i++)
@ -177,8 +180,11 @@ void C53C810::Run(bool singleStep)
// Issue IRQ and finish
Ctx.regISTAT |= 1; // DMA interrupt pending
Ctx.regDSTAT |= 8; // single step interrupt
Ctx.IRQ->Assert(Ctx.scsiIRQ); // generate an interrupt
DebugLog("53C810: Asserted IRQ\n");
if (Ctx.regDIEN & 8)
{
Ctx.IRQ->Assert(Ctx.scsiIRQ); // generate an interrupt
DebugLog("53C810: Asserted IRQ\n");
}
}
else
{
@ -317,6 +323,9 @@ void C53C810::WriteRegister(unsigned reg, UINT8 data)
case 0x38: // DMODE
Ctx.regDMODE = data;
break;
case 0x39: // DIEN
Ctx.regDIEN = data;
break;
case 0x3B: // DCNTL
Ctx.regDCNTL = data;
if ((Ctx.regDCNTL&0x14) == 0x14) // single step
@ -396,8 +405,10 @@ UINT8 C53C810::ReadRegister(unsigned reg)
return (Ctx.regDSPS>>16)&0xFF;
case 0x33: // DSPS 31-24
return (Ctx.regDSPS>>24)&0xFF;
case 0x38:
case 0x38: // DMODE
return Ctx.regDMODE;
case 0x39: // DIEN
return Ctx.regDIEN;
case 0x3B: // DCNTL
return Ctx.regDCNTL;
default: // get it from the register file
@ -469,6 +480,7 @@ void C53C810::Reset(void)
Ctx.regDCNTL = 0;
Ctx.regDMODE = 0;
Ctx.regDSTAT = 0x80; // DMA FIFO empty
Ctx.regDIEN = 0;
Ctx.regISTAT = 0;
Ctx.halt = false;

View file

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

View file

@ -69,17 +69,16 @@ void CIRQ::Assert(unsigned irqBits)
{
irqState |= irqBits;
if ((irqState&irqEnable)) // low 8 bits are maskable interrupts
//ppc_set_irq_line(0);
ppc_set_irq_line(1);
if ((irqState&(~0xFF))) // any non-maskable interrupts pending?
//ppc_set_irq_line(0);
ppc_set_irq_line(1);
}
//TO-DO: CPU needs to have deassert logic!
void CIRQ::Deassert(unsigned 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)

View file

@ -840,13 +840,8 @@ void CModel3::WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits,
/******************************************************************************
Model 3 System Registers
NOTE: Proper IRQ handling requires a "deassert" function in the PowerPC core,
which the interpreter presently lacks. This is because different modules that
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!
NOTE: Different modules that generate IRQs, like the tilegen, Real3D, and
SCSP, should each call IRQ.Assert() on their own.
******************************************************************************/
// Set the CROM bank index (active low logic)
@ -2003,16 +1998,7 @@ void CModel3::RunFrame(void)
#ifdef NET_BOARD
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();
// 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
}
else
@ -2026,16 +2012,7 @@ void CModel3::RunFrame(void)
RunDriveBoardFrame();
#ifdef NET_BOARD
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();
// 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
}

View file

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