mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2025-02-16 17:35:39 +00:00
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:
parent
9ffce8b92a
commit
e93c5d710f
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue