mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-25 23:25:40 +00:00
53c810: do not increment DSP when executing interrupt instruction. Fixes invalid polygons in Virtua Fighter 3. Thanks to Ian Curtis for discovering that the polygons were incorrectly-copied garbage.
This commit is contained in:
parent
8e6712d2c7
commit
8f87bb1698
|
@ -24,27 +24,22 @@
|
|||
*
|
||||
* Implementation of the C53C810 class: NCR 53C810 SCSI controller.
|
||||
*
|
||||
* TO-DO List:
|
||||
* -----------
|
||||
* Notes:
|
||||
* ------
|
||||
* - VF3 does something weird: it writes DSP (triggering automatic code
|
||||
* execution because MAN=0) and THEN sets single step mode, expecting an
|
||||
* interrupt to occur. I suspect this is incorrect operation and that
|
||||
* the SCRIPTS processor either enters single-step mode while the memory
|
||||
* transfer is underway (though it is unlikely because it's such a short
|
||||
* transfer), or that single step can occur even when the device is "halted"
|
||||
* (which would mean the SCRIPTS processor executes an invalid instruction
|
||||
* (which would mean the SCRIPTS processor executes an invalid instruction
|
||||
* once or twice without the VF3 SCSI driver noticing). Unfortunately, the
|
||||
* former is not feasible to emulate. If automatic SCRIPTS execution is
|
||||
* disabled when single-stepping is enabled, Scud Race breaks (glitchy,
|
||||
* jerky graphics). Enabling automatic execution and also allowing single
|
||||
* stepping to occur when the processor is halted seems to work, but it
|
||||
* causes invalid instructions to be hit each time.
|
||||
* - Check to ensure the invalid instructions hit above would never be decoded
|
||||
* as real SCRIPTS instructions (in some cases, appears they can be...)
|
||||
* - Is the SIP bit supposed to be set after single stepping?
|
||||
* disabled when single-stepping is enabled, Scud Race breaks (glitchy,
|
||||
* jerky graphics). Enabling automatic execution and also allowing single
|
||||
* stepping to occur when the processor is halted seems to work, but it
|
||||
* causes invalid instructions to be hit each time.
|
||||
* - pg 2-22 (42) of the manual has description of how to clear interrupts.
|
||||
* - Another way to fix VF3 is to just set the single-step and DMA interrupt
|
||||
* flags at all times.
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -58,37 +53,37 @@
|
|||
|
||||
void C53C810::SaveState(CBlockFile *SaveState)
|
||||
{
|
||||
SaveState->NewBlock("53C810", __FILE__);
|
||||
SaveState->Write(Ctx.regs, sizeof(Ctx.regs));
|
||||
SaveState->Write(&Ctx.regTEMP, sizeof(Ctx.regTEMP));
|
||||
SaveState->Write(&Ctx.regDSP, sizeof(Ctx.regDSP));
|
||||
SaveState->Write(&Ctx.regDSPS, sizeof(Ctx.regDSPS));
|
||||
SaveState->Write(&Ctx.regDBC, sizeof(Ctx.regDBC));
|
||||
SaveState->Write(&Ctx.regDCMD, sizeof(Ctx.regDCMD));
|
||||
SaveState->Write(&Ctx.regDCNTL, sizeof(Ctx.regDCNTL));
|
||||
SaveState->Write(&Ctx.regDMODE, sizeof(Ctx.regDMODE));
|
||||
SaveState->Write(&Ctx.regDSTAT, sizeof(Ctx.regDSTAT));
|
||||
SaveState->Write(&Ctx.regISTAT, sizeof(Ctx.regISTAT));
|
||||
SaveState->NewBlock("53C810", __FILE__);
|
||||
SaveState->Write(Ctx.regs, sizeof(Ctx.regs));
|
||||
SaveState->Write(&Ctx.regTEMP, sizeof(Ctx.regTEMP));
|
||||
SaveState->Write(&Ctx.regDSP, sizeof(Ctx.regDSP));
|
||||
SaveState->Write(&Ctx.regDSPS, sizeof(Ctx.regDSPS));
|
||||
SaveState->Write(&Ctx.regDBC, sizeof(Ctx.regDBC));
|
||||
SaveState->Write(&Ctx.regDCMD, sizeof(Ctx.regDCMD));
|
||||
SaveState->Write(&Ctx.regDCNTL, sizeof(Ctx.regDCNTL));
|
||||
SaveState->Write(&Ctx.regDMODE, sizeof(Ctx.regDMODE));
|
||||
SaveState->Write(&Ctx.regDSTAT, sizeof(Ctx.regDSTAT));
|
||||
SaveState->Write(&Ctx.regISTAT, sizeof(Ctx.regISTAT));
|
||||
}
|
||||
|
||||
void C53C810::LoadState(CBlockFile *SaveState)
|
||||
{
|
||||
if (OKAY != SaveState->FindBlock("53C810"))
|
||||
{
|
||||
ErrorLog("Unable to load 53C810 state. Save state file is corrupt.");
|
||||
return;
|
||||
}
|
||||
|
||||
SaveState->Read(Ctx.regs, sizeof(Ctx.regs));
|
||||
SaveState->Read(&Ctx.regTEMP, sizeof(Ctx.regTEMP));
|
||||
SaveState->Read(&Ctx.regDSP, sizeof(Ctx.regDSP));
|
||||
SaveState->Read(&Ctx.regDSPS, sizeof(Ctx.regDSPS));
|
||||
SaveState->Read(&Ctx.regDBC, sizeof(Ctx.regDBC));
|
||||
SaveState->Read(&Ctx.regDCMD, sizeof(Ctx.regDCMD));
|
||||
SaveState->Read(&Ctx.regDCNTL, sizeof(Ctx.regDCNTL));
|
||||
SaveState->Read(&Ctx.regDMODE, sizeof(Ctx.regDMODE));
|
||||
SaveState->Read(&Ctx.regDSTAT, sizeof(Ctx.regDSTAT));
|
||||
SaveState->Read(&Ctx.regISTAT, sizeof(Ctx.regISTAT));
|
||||
if (OKAY != SaveState->FindBlock("53C810"))
|
||||
{
|
||||
ErrorLog("Unable to load 53C810 state. Save state file is corrupt.");
|
||||
return;
|
||||
}
|
||||
|
||||
SaveState->Read(Ctx.regs, sizeof(Ctx.regs));
|
||||
SaveState->Read(&Ctx.regTEMP, sizeof(Ctx.regTEMP));
|
||||
SaveState->Read(&Ctx.regDSP, sizeof(Ctx.regDSP));
|
||||
SaveState->Read(&Ctx.regDSPS, sizeof(Ctx.regDSPS));
|
||||
SaveState->Read(&Ctx.regDBC, sizeof(Ctx.regDBC));
|
||||
SaveState->Read(&Ctx.regDCMD, sizeof(Ctx.regDCMD));
|
||||
SaveState->Read(&Ctx.regDCNTL, sizeof(Ctx.regDCNTL));
|
||||
SaveState->Read(&Ctx.regDMODE, sizeof(Ctx.regDMODE));
|
||||
SaveState->Read(&Ctx.regDSTAT, sizeof(Ctx.regDSTAT));
|
||||
SaveState->Read(&Ctx.regISTAT, sizeof(Ctx.regISTAT));
|
||||
}
|
||||
|
||||
|
||||
|
@ -96,127 +91,127 @@ void C53C810::LoadState(CBlockFile *SaveState)
|
|||
SCRIPTS Emulation
|
||||
******************************************************************************/
|
||||
|
||||
static inline UINT32 Fetch(struct NCR53C810Context *Ctx)
|
||||
static inline UINT32 Fetch(struct NCR53C810Context *Ctx, UINT32 offset)
|
||||
{
|
||||
UINT32 data = Ctx->Bus->Read32(Ctx->regDSP);
|
||||
Ctx->regDSP += 4;
|
||||
return FLIPENDIAN32(data); // remember: bus is big endian, need to convert to little endian
|
||||
UINT32 data = Ctx->Bus->Read32(Ctx->regDSP + offset);
|
||||
return FLIPENDIAN32(data); // remember: bus is big endian, need to convert to little endian
|
||||
}
|
||||
|
||||
//TO-DO: check if this ever occurs in single-step mode (if so, we would need to stack interrupts)
|
||||
//TODO: what happens if interrupt is executed in single step mode?
|
||||
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->regDBC&0x100000)) // INTFLY
|
||||
return ErrorLog("53C810 INTFLY instruction not emulated!");
|
||||
return OKAY;
|
||||
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->regDBC&0x100000)) // INTFLY
|
||||
return ErrorLog("53C810 INTFLY instruction not emulated!");
|
||||
// DSP not incremented (VF3 relies on this)
|
||||
return OKAY;
|
||||
}
|
||||
|
||||
static bool SCRIPTS_MoveMemory(struct NCR53C810Context *Ctx)
|
||||
{
|
||||
UINT32 src, dest;
|
||||
unsigned numBytes, i;
|
||||
UINT32 src, dest;
|
||||
unsigned numBytes, i;
|
||||
|
||||
// Get operands
|
||||
src = Ctx->regDSPS;
|
||||
dest = Ctx->regTEMP = Fetch(Ctx);
|
||||
numBytes = Ctx->regDBC;
|
||||
// not implemented: illegal instruction interrupt when src and dest are not aligned the same way
|
||||
// Get operands
|
||||
src = Ctx->regDSPS;
|
||||
dest = Ctx->regTEMP = Fetch(Ctx, 8); // word 3
|
||||
numBytes = Ctx->regDBC;
|
||||
// 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);
|
||||
|
||||
// Perform a 32-bit copy if possible
|
||||
for (i = 0; i < (numBytes/4); i++)
|
||||
{
|
||||
Ctx->Bus->Write32(dest, Ctx->Bus->Read32(src));
|
||||
dest += 4;
|
||||
src += 4;
|
||||
}
|
||||
// Perform a 32-bit copy if possible
|
||||
for (i = 0; i < (numBytes/4); i++)
|
||||
{
|
||||
Ctx->Bus->Write32(dest, Ctx->Bus->Read32(src));
|
||||
dest += 4;
|
||||
src += 4;
|
||||
}
|
||||
|
||||
// Finish off the last few odd bytes
|
||||
numBytes &= 3;
|
||||
while (numBytes)
|
||||
{
|
||||
Ctx->Bus->Write8(dest++, Ctx->Bus->Read8(src++));
|
||||
--numBytes;
|
||||
}
|
||||
// Finish off the last few odd bytes
|
||||
numBytes &= 3;
|
||||
while (numBytes)
|
||||
{
|
||||
Ctx->Bus->Write8(dest++, Ctx->Bus->Read8(src++));
|
||||
--numBytes;
|
||||
}
|
||||
|
||||
// Update registers
|
||||
Ctx->regDBC = 0;
|
||||
Ctx->regDSPS = src;
|
||||
Ctx->regTEMP = dest;
|
||||
// Update registers
|
||||
Ctx->regDBC = 0;
|
||||
Ctx->regDSPS = src;
|
||||
Ctx->regTEMP = dest;
|
||||
Ctx->regDSP += 12;
|
||||
|
||||
return OKAY;
|
||||
return OKAY;
|
||||
}
|
||||
|
||||
// Invalid instruction handler
|
||||
static bool SCRIPTS_Invalid(struct NCR53C810Context *Ctx)
|
||||
{
|
||||
DebugLog("53C810 encountered an unrecognized instruction (%02X%06X, DSP=%08X)\n!", Ctx->regDCMD, Ctx->regDBC, Ctx->regDSP);
|
||||
return FAIL;
|
||||
DebugLog("53C810 encountered an unrecognized instruction (%02X%06X, DSP=%08X)\n!", Ctx->regDCMD, Ctx->regDBC, Ctx->regDSP);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
void C53C810::Run(bool singleStep)
|
||||
{
|
||||
UINT32 op;
|
||||
int i;
|
||||
|
||||
if (singleStep)// && !Ctx.halt)
|
||||
{
|
||||
// Fetch instruction (first two words are always fetched)
|
||||
op = Fetch(&Ctx); // word 1
|
||||
Ctx.regDBC = op&0x00FFFFFF;
|
||||
Ctx.regDCMD = (op>>24)&0xFF;
|
||||
Ctx.regDSPS = Fetch(&Ctx); // word 2
|
||||
|
||||
// Single step
|
||||
OpTable[Ctx.regDCMD](&Ctx);
|
||||
|
||||
// Issue IRQ and finish
|
||||
Ctx.regISTAT |= 3; // DMA interrupt pending (NOTE: should SIP be set? I don't think so...)
|
||||
Ctx.regDSTAT |= 8; // single step interrupt
|
||||
Ctx.IRQ->Assert(Ctx.scsiIRQ); // generate an interrupt
|
||||
DebugLog("53C810: Asserted IRQ\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Automatic mode: run (as long as the processor is not halted)
|
||||
for (i = 0; (i < 100) && !Ctx.halt; i++)
|
||||
{
|
||||
// Fetch instruction (first two words are always fetched)
|
||||
op = Fetch(&Ctx); // word 1
|
||||
Ctx.regDBC = op&0x00FFFFFF;
|
||||
Ctx.regDCMD = (op>>24)&0xFF;
|
||||
Ctx.regDSPS = Fetch(&Ctx); // word 2
|
||||
|
||||
// Execute!
|
||||
if (OpTable[Ctx.regDCMD](&Ctx) != OKAY)
|
||||
break;
|
||||
}
|
||||
}
|
||||
UINT32 op;
|
||||
int i;
|
||||
|
||||
if (singleStep)// && !Ctx.halt)
|
||||
{
|
||||
// Fetch instruction (first two words are always fetched)
|
||||
op = Fetch(&Ctx, 0); // word 1
|
||||
Ctx.regDBC = op&0x00FFFFFF;
|
||||
Ctx.regDCMD = (op>>24)&0xFF;
|
||||
Ctx.regDSPS = Fetch(&Ctx, 4); // word 2
|
||||
|
||||
// Single step
|
||||
OpTable[Ctx.regDCMD](&Ctx);
|
||||
|
||||
// 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");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Automatic mode: run (as long as the processor is not halted)
|
||||
for (i = 0; (i < 100) && !Ctx.halt; i++)
|
||||
{
|
||||
// Fetch instruction (first two words are always fetched)
|
||||
op = Fetch(&Ctx, 0); // word 1
|
||||
Ctx.regDBC = op&0x00FFFFFF;
|
||||
Ctx.regDCMD = (op>>24)&0xFF;
|
||||
Ctx.regDSPS = Fetch(&Ctx, 4); // word 2
|
||||
|
||||
// Execute!
|
||||
if (OpTable[Ctx.regDCMD](&Ctx) != OKAY)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert instructions into the LUT under control of the mask
|
||||
void C53C810::Insert(UINT8 mask, UINT8 op, bool (*Handler)(struct NCR53C810Context *))
|
||||
{
|
||||
UINT32 i;
|
||||
UINT32 i;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
if ((i&mask) == op)
|
||||
OpTable[i] = Handler;
|
||||
}
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
if ((i&mask) == op)
|
||||
OpTable[i] = Handler;
|
||||
}
|
||||
}
|
||||
|
||||
void C53C810::BuildOpTable(void)
|
||||
{
|
||||
Insert(0, 0, &SCRIPTS_Invalid);
|
||||
Insert(0xE0, 0xC0, &SCRIPTS_MoveMemory);
|
||||
Insert(0xF8, 0x98, &SCRIPTS_Int_IntFly);
|
||||
Insert(0, 0, &SCRIPTS_Invalid);
|
||||
Insert(0xE0, 0xC0, &SCRIPTS_MoveMemory);
|
||||
Insert(0xF8, 0x98, &SCRIPTS_Int_IntFly);
|
||||
}
|
||||
|
||||
|
||||
|
@ -226,255 +221,255 @@ void C53C810::BuildOpTable(void)
|
|||
|
||||
void C53C810::WriteRegister(unsigned reg, UINT8 data)
|
||||
{
|
||||
if (reg >= 0x60)
|
||||
{
|
||||
ErrorLog("Write to invalid 53C810 register (%02X).", reg);
|
||||
return;
|
||||
}
|
||||
|
||||
DebugLog("53C810 write: %02X=%02X (PC=%08X, LR=%08X)\n", reg, data, ppc_get_pc(), ppc_get_lr());
|
||||
|
||||
// Dump everything into the register file
|
||||
Ctx.regs[reg&0xFF] = data;
|
||||
|
||||
// Do something extra with the ones that we actually care about
|
||||
// TO-DO: prevent invalid/reserved/read-only registers from being written?
|
||||
switch(reg)
|
||||
{
|
||||
case 0x14: // ISTAT
|
||||
Ctx.regISTAT = data;
|
||||
DebugLog("ISTAT=%02X\n", data);
|
||||
break;
|
||||
case 0x1C: // TEMP 7-0
|
||||
Ctx.regTEMP &= 0xFFFFFF00;
|
||||
Ctx.regTEMP |= data;
|
||||
break;
|
||||
case 0x1D: // TEMP 15-8
|
||||
Ctx.regTEMP &= 0xFFFF00FF;
|
||||
Ctx.regTEMP |= (data<<8);
|
||||
break;
|
||||
case 0x1E: // TEMP 23-16
|
||||
Ctx.regTEMP &= 0xFF00FFFF;
|
||||
Ctx.regTEMP |= (data<<16);
|
||||
break;
|
||||
case 0x1F: // TEMP 31-24
|
||||
Ctx.regTEMP &= 0x00FFFFFF;
|
||||
Ctx.regTEMP |= (data<<24);
|
||||
break;
|
||||
case 0x24: // DBC 7-0
|
||||
Ctx.regDBC &= 0xFFFFFF00;
|
||||
Ctx.regDBC |= data;
|
||||
break;
|
||||
case 0x25: // DBC 15-8
|
||||
Ctx.regDBC &= 0xFFFF00FF;
|
||||
Ctx.regDBC |= (data<<8);
|
||||
break;
|
||||
case 0x26: // DBC 23-16
|
||||
Ctx.regDBC &= 0xFF00FFFF;
|
||||
Ctx.regDBC |= (data<<16);
|
||||
break;
|
||||
case 0x27: // DCMD
|
||||
Ctx.regDCMD = data;
|
||||
break;
|
||||
case 0x2C: // DSP 7-0
|
||||
Ctx.regDSP &= 0xFFFFFF00;
|
||||
Ctx.regDSP |= data;
|
||||
break;
|
||||
case 0x2D: // DSP 15-8
|
||||
Ctx.regDSP &= 0xFFFF00FF;
|
||||
Ctx.regDSP |= (data<<8);
|
||||
break;
|
||||
case 0x2E: // DSP 23-16
|
||||
Ctx.regDSP &= 0xFF00FFFF;
|
||||
Ctx.regDSP |= (data<<16);
|
||||
break;
|
||||
case 0x2F: // DSP 31-24
|
||||
Ctx.regDSP &= 0x00FFFFFF;
|
||||
Ctx.regDSP |= (data<<24);
|
||||
Ctx.halt = false; // writing this register un-halts 53C810 operation (pg.6-31 of LSI manual)
|
||||
if (!(Ctx.regDMODE&1)) // if MAN=0, start SCRIPTS automatically
|
||||
// To-Do: is this correct? Should single step really be tested first?
|
||||
//if (!(Ctx.regDCNTL&0x10) && !(Ctx.regDMODE&1)) // if MAN=0 and not single stepping, start SCRIPTS automatically
|
||||
{
|
||||
DebugLog("53C810: Automatically starting (PC=%08X, LR=%08X, single step=%d)\n", ppc_get_pc(), ppc_get_lr(), !!(Ctx.regDCNTL&0x10));
|
||||
Run(false); // automatic
|
||||
}
|
||||
break;
|
||||
case 0x30: // DSPS 7-0
|
||||
Ctx.regDSPS &= 0xFFFFFF00;
|
||||
Ctx.regDSPS |= data;
|
||||
break;
|
||||
case 0x31: // DSPS 15-8
|
||||
Ctx.regDSPS &= 0xFFFF00FF;
|
||||
Ctx.regDSPS |= (data<<8);
|
||||
break;
|
||||
case 0x32: // DSPS 23-16
|
||||
Ctx.regDSPS &= 0xFF00FFFF;
|
||||
Ctx.regDSPS |= (data<<16);
|
||||
break;
|
||||
case 0x33: // DSPS 31-24
|
||||
Ctx.regDSPS &= 0x00FFFFFF;
|
||||
Ctx.regDSPS |= (data<<24);
|
||||
break;
|
||||
case 0x38: // DMODE
|
||||
Ctx.regDMODE = data;
|
||||
break;
|
||||
case 0x3B: // DCNTL
|
||||
Ctx.regDCNTL = data;
|
||||
if ((Ctx.regDCNTL&0x14) == 0x14) // single step
|
||||
{
|
||||
DebugLog("53C810: single step: %08X, (halt=%d)\n", Ctx.regDSP, Ctx.halt);
|
||||
Run(true);
|
||||
}
|
||||
else if ((Ctx.regDCNTL&0x04)) // start DMA bit
|
||||
{
|
||||
DebugLog("53C810: Manually starting\n");
|
||||
Run(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (reg >= 0x60)
|
||||
{
|
||||
ErrorLog("Write to invalid 53C810 register (%02X).", reg);
|
||||
return;
|
||||
}
|
||||
|
||||
DebugLog("53C810 write: %02X=%02X (PC=%08X, LR=%08X)\n", reg, data, ppc_get_pc(), ppc_get_lr());
|
||||
|
||||
// Dump everything into the register file
|
||||
Ctx.regs[reg&0xFF] = data;
|
||||
|
||||
// Do something extra with the ones that we actually care about
|
||||
// TO-DO: prevent invalid/reserved/read-only registers from being written?
|
||||
switch(reg)
|
||||
{
|
||||
case 0x14: // ISTAT
|
||||
Ctx.regISTAT = data;
|
||||
DebugLog("ISTAT=%02X\n", data);
|
||||
break;
|
||||
case 0x1C: // TEMP 7-0
|
||||
Ctx.regTEMP &= 0xFFFFFF00;
|
||||
Ctx.regTEMP |= data;
|
||||
break;
|
||||
case 0x1D: // TEMP 15-8
|
||||
Ctx.regTEMP &= 0xFFFF00FF;
|
||||
Ctx.regTEMP |= (data<<8);
|
||||
break;
|
||||
case 0x1E: // TEMP 23-16
|
||||
Ctx.regTEMP &= 0xFF00FFFF;
|
||||
Ctx.regTEMP |= (data<<16);
|
||||
break;
|
||||
case 0x1F: // TEMP 31-24
|
||||
Ctx.regTEMP &= 0x00FFFFFF;
|
||||
Ctx.regTEMP |= (data<<24);
|
||||
break;
|
||||
case 0x24: // DBC 7-0
|
||||
Ctx.regDBC &= 0xFFFFFF00;
|
||||
Ctx.regDBC |= data;
|
||||
break;
|
||||
case 0x25: // DBC 15-8
|
||||
Ctx.regDBC &= 0xFFFF00FF;
|
||||
Ctx.regDBC |= (data<<8);
|
||||
break;
|
||||
case 0x26: // DBC 23-16
|
||||
Ctx.regDBC &= 0xFF00FFFF;
|
||||
Ctx.regDBC |= (data<<16);
|
||||
break;
|
||||
case 0x27: // DCMD
|
||||
Ctx.regDCMD = data;
|
||||
break;
|
||||
case 0x2C: // DSP 7-0
|
||||
Ctx.regDSP &= 0xFFFFFF00;
|
||||
Ctx.regDSP |= data;
|
||||
break;
|
||||
case 0x2D: // DSP 15-8
|
||||
Ctx.regDSP &= 0xFFFF00FF;
|
||||
Ctx.regDSP |= (data<<8);
|
||||
break;
|
||||
case 0x2E: // DSP 23-16
|
||||
Ctx.regDSP &= 0xFF00FFFF;
|
||||
Ctx.regDSP |= (data<<16);
|
||||
break;
|
||||
case 0x2F: // DSP 31-24
|
||||
Ctx.regDSP &= 0x00FFFFFF;
|
||||
Ctx.regDSP |= (data<<24);
|
||||
Ctx.halt = false; // writing this register un-halts 53C810 operation (pg.6-31 of LSI manual)
|
||||
if (!(Ctx.regDMODE&1)) // if MAN=0, start SCRIPTS automatically
|
||||
// To-Do: is this correct? Should single step really be tested first?
|
||||
//if (!(Ctx.regDCNTL&0x10) && !(Ctx.regDMODE&1)) // if MAN=0 and not single stepping, start SCRIPTS automatically
|
||||
{
|
||||
DebugLog("53C810: Automatically starting (PC=%08X, LR=%08X, single step=%d)\n", ppc_get_pc(), ppc_get_lr(), !!(Ctx.regDCNTL&0x10));
|
||||
Run(false); // automatic
|
||||
}
|
||||
break;
|
||||
case 0x30: // DSPS 7-0
|
||||
Ctx.regDSPS &= 0xFFFFFF00;
|
||||
Ctx.regDSPS |= data;
|
||||
break;
|
||||
case 0x31: // DSPS 15-8
|
||||
Ctx.regDSPS &= 0xFFFF00FF;
|
||||
Ctx.regDSPS |= (data<<8);
|
||||
break;
|
||||
case 0x32: // DSPS 23-16
|
||||
Ctx.regDSPS &= 0xFF00FFFF;
|
||||
Ctx.regDSPS |= (data<<16);
|
||||
break;
|
||||
case 0x33: // DSPS 31-24
|
||||
Ctx.regDSPS &= 0x00FFFFFF;
|
||||
Ctx.regDSPS |= (data<<24);
|
||||
break;
|
||||
case 0x38: // DMODE
|
||||
Ctx.regDMODE = data;
|
||||
break;
|
||||
case 0x3B: // DCNTL
|
||||
Ctx.regDCNTL = data;
|
||||
if ((Ctx.regDCNTL&0x14) == 0x14) // single step
|
||||
{
|
||||
DebugLog("53C810: single step: %08X, (halt=%d)\n", Ctx.regDSP, Ctx.halt);
|
||||
Run(true);
|
||||
}
|
||||
else if ((Ctx.regDCNTL&0x04)) // start DMA bit
|
||||
{
|
||||
DebugLog("53C810: Manually starting\n");
|
||||
Run(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UINT8 C53C810::ReadRegister(unsigned reg)
|
||||
{
|
||||
UINT8 ret;
|
||||
|
||||
if (reg >= 0x60)
|
||||
{
|
||||
ErrorLog("Read from invalid 53C810 register (%02X).", reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DebugLog("53C810 read: %02X (PC=%08X, LR=%08X)\n", reg, ppc_get_pc(), ppc_get_lr());
|
||||
|
||||
// Some registers require special handling
|
||||
switch(reg)
|
||||
{
|
||||
case 0x0C: // DSTAT
|
||||
// For now, we don't generate stacked interrupts, so always clear IRQ status
|
||||
ret = Ctx.regDSTAT;
|
||||
//TO-DO: manual says these should be cleared here but MAME never clears them. What's up with that?
|
||||
Ctx.regISTAT &= 0xFE; // clear DIP bit (DMA interrupt)
|
||||
Ctx.regDSTAT &= 0xF7; // clear SSI (single step interrupt)
|
||||
//Ctx.regISTAT |= 1; // doing this is another way to fix VF3
|
||||
//Ctx.regDSTAT |= 8;
|
||||
Ctx.IRQ->Deassert(Ctx.scsiIRQ);
|
||||
//DebugLog("53C810: DSTAT read\n");
|
||||
return ret;
|
||||
case 0x14: // ISTAT
|
||||
//DebugLog("53C810: ISTAT read\n");
|
||||
return Ctx.regISTAT;
|
||||
case 0x1C: // TEMP 7-0
|
||||
return Ctx.regTEMP&0xFF;
|
||||
case 0x1D: // TEMP 15-8
|
||||
return (Ctx.regTEMP>>8)&0xFF;
|
||||
case 0x1E: // TEMP 23-16
|
||||
return (Ctx.regTEMP>>16)&0xFF;
|
||||
case 0x1F: // TEMP 31-24
|
||||
return (Ctx.regTEMP>>24)&0xFF;
|
||||
case 0x24: // DBC 7-0
|
||||
return Ctx.regDBC&0xFF;
|
||||
case 0x25: // DBC 15-8
|
||||
return (Ctx.regDBC>>8)&0xFF;
|
||||
case 0x26: // DBC 23-16
|
||||
return (Ctx.regDBC>>16)&0xFF;
|
||||
case 0x27: // DCMD
|
||||
return Ctx.regDCMD;
|
||||
case 0x2C: // DSP 7-0
|
||||
return Ctx.regDSP&0xFF;
|
||||
case 0x2D: // DSP 15-8
|
||||
return (Ctx.regDSP>>8)&0xFF;
|
||||
case 0x2E: // DSP 23-16
|
||||
return (Ctx.regDSP>>16)&0xFF;
|
||||
case 0x2F: // DSP 31-24
|
||||
return (Ctx.regDSP>>24)&0xFF;
|
||||
case 0x30: // DSPS 7-0
|
||||
return Ctx.regDSPS&0xFF;
|
||||
case 0x31: // DSPS 15-8
|
||||
return (Ctx.regDSPS>>8)&0xFF;
|
||||
case 0x32: // DSPS 23-16
|
||||
return (Ctx.regDSPS>>16)&0xFF;
|
||||
case 0x33: // DSPS 31-24
|
||||
return (Ctx.regDSPS>>24)&0xFF;
|
||||
case 0x38:
|
||||
return Ctx.regDMODE;
|
||||
case 0x3B: // DCNTL
|
||||
return Ctx.regDCNTL;
|
||||
default: // get it from the register file
|
||||
break;
|
||||
}
|
||||
|
||||
// Register file should be up to date
|
||||
return Ctx.regs[reg&0xFF];
|
||||
}
|
||||
UINT8 ret;
|
||||
|
||||
if (reg >= 0x60)
|
||||
{
|
||||
ErrorLog("Read from invalid 53C810 register (%02X).", reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DebugLog("53C810 read: %02X (PC=%08X, LR=%08X)\n", reg, ppc_get_pc(), ppc_get_lr());
|
||||
|
||||
// Some registers require special handling
|
||||
switch(reg)
|
||||
{
|
||||
case 0x0C: // DSTAT
|
||||
// For now, we don't generate stacked interrupts, so always clear IRQ status
|
||||
ret = Ctx.regDSTAT;
|
||||
//TO-DO: manual says these should be cleared here but MAME never clears them. What's up with that?
|
||||
Ctx.regISTAT &= 0xFE; // clear DIP bit (DMA interrupt)
|
||||
Ctx.regDSTAT &= 0xF7; // clear SSI (single step interrupt)
|
||||
//Ctx.regISTAT |= 1; // doing this is another way to fix VF3
|
||||
//Ctx.regDSTAT |= 8;
|
||||
Ctx.IRQ->Deassert(Ctx.scsiIRQ);
|
||||
//DebugLog("53C810: DSTAT read\n");
|
||||
return ret;
|
||||
case 0x14: // ISTAT
|
||||
//DebugLog("53C810: ISTAT read\n");
|
||||
return Ctx.regISTAT;
|
||||
case 0x1C: // TEMP 7-0
|
||||
return Ctx.regTEMP&0xFF;
|
||||
case 0x1D: // TEMP 15-8
|
||||
return (Ctx.regTEMP>>8)&0xFF;
|
||||
case 0x1E: // TEMP 23-16
|
||||
return (Ctx.regTEMP>>16)&0xFF;
|
||||
case 0x1F: // TEMP 31-24
|
||||
return (Ctx.regTEMP>>24)&0xFF;
|
||||
case 0x24: // DBC 7-0
|
||||
return Ctx.regDBC&0xFF;
|
||||
case 0x25: // DBC 15-8
|
||||
return (Ctx.regDBC>>8)&0xFF;
|
||||
case 0x26: // DBC 23-16
|
||||
return (Ctx.regDBC>>16)&0xFF;
|
||||
case 0x27: // DCMD
|
||||
return Ctx.regDCMD;
|
||||
case 0x2C: // DSP 7-0
|
||||
return Ctx.regDSP&0xFF;
|
||||
case 0x2D: // DSP 15-8
|
||||
return (Ctx.regDSP>>8)&0xFF;
|
||||
case 0x2E: // DSP 23-16
|
||||
return (Ctx.regDSP>>16)&0xFF;
|
||||
case 0x2F: // DSP 31-24
|
||||
return (Ctx.regDSP>>24)&0xFF;
|
||||
case 0x30: // DSPS 7-0
|
||||
return Ctx.regDSPS&0xFF;
|
||||
case 0x31: // DSPS 15-8
|
||||
return (Ctx.regDSPS>>8)&0xFF;
|
||||
case 0x32: // DSPS 23-16
|
||||
return (Ctx.regDSPS>>16)&0xFF;
|
||||
case 0x33: // DSPS 31-24
|
||||
return (Ctx.regDSPS>>24)&0xFF;
|
||||
case 0x38:
|
||||
return Ctx.regDMODE;
|
||||
case 0x3B: // DCNTL
|
||||
return Ctx.regDCNTL;
|
||||
default: // get it from the register file
|
||||
break;
|
||||
}
|
||||
|
||||
// Register file should be up to date
|
||||
return Ctx.regs[reg&0xFF];
|
||||
}
|
||||
|
||||
UINT32 C53C810::ReadPCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned offset)
|
||||
{
|
||||
UINT32 d;
|
||||
|
||||
if ((bits==8))
|
||||
{
|
||||
DebugLog("53C810 %d-bit PCI read request for reg=%02X\n", bits, reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (reg)
|
||||
{
|
||||
case 0x00: // Device ID and Vendor ID
|
||||
d = 0x00011000; // 0x1000 = LSI Logic
|
||||
switch (bits)
|
||||
{
|
||||
case 8:
|
||||
d >>= (3-offset)*8; // offset will be 0-3; select appropriate byte
|
||||
d &= 0xFF;
|
||||
break;
|
||||
case 16:
|
||||
d >>= (2-offset)*8; // offset will be 0 or 2 only; select either high or low word
|
||||
d &= 0xFFFF;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return d;
|
||||
default:
|
||||
DebugLog("53C810 PCI read request for reg=%02X (%d-bit)\n", reg, bits);
|
||||
break;
|
||||
}
|
||||
UINT32 d;
|
||||
|
||||
if ((bits==8))
|
||||
{
|
||||
DebugLog("53C810 %d-bit PCI read request for reg=%02X\n", bits, reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (reg)
|
||||
{
|
||||
case 0x00: // Device ID and Vendor ID
|
||||
d = 0x00011000; // 0x1000 = LSI Logic
|
||||
switch (bits)
|
||||
{
|
||||
case 8:
|
||||
d >>= (3-offset)*8; // offset will be 0-3; select appropriate byte
|
||||
d &= 0xFF;
|
||||
break;
|
||||
case 16:
|
||||
d >>= (2-offset)*8; // offset will be 0 or 2 only; select either high or low word
|
||||
d &= 0xFFFF;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return d;
|
||||
default:
|
||||
DebugLog("53C810 PCI read request for reg=%02X (%d-bit)\n", reg, bits);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void C53C810::WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned offset, UINT32 data)
|
||||
{
|
||||
DebugLog("53C810 PCI %d-bit write request for reg=%02X, data=%08X\n", bits, reg, data);
|
||||
DebugLog("53C810 PCI %d-bit write request for reg=%02X, data=%08X\n", bits, reg, data);
|
||||
}
|
||||
|
||||
void C53C810::Reset(void)
|
||||
{
|
||||
memset(Ctx.regs, 0, sizeof(Ctx.regs));
|
||||
Ctx.regs[0x00] = 0xC0; // SCNTL0
|
||||
Ctx.regs[0x0C] = 0x80; // DSTAT
|
||||
Ctx.regs[0x0F] = 0x02; // SSTAT2
|
||||
Ctx.regs[0x18] = 0xFF; // reserved
|
||||
Ctx.regs[0x19] = 0xF0; // CTEST1
|
||||
Ctx.regs[0x1A] = 0x01; // CTEST2
|
||||
Ctx.regs[0x46] = 0x60; // MACNTL
|
||||
Ctx.regs[0x47] = 0x0F; // GPCNTL
|
||||
Ctx.regs[0x4C] = 0x03; // STEST0
|
||||
Ctx.regTEMP = 0;
|
||||
Ctx.regDSP = 0;
|
||||
Ctx.regDSPS = 0;
|
||||
Ctx.regDBC = 0;
|
||||
Ctx.regDCMD = 0;
|
||||
Ctx.regDCNTL = 0;
|
||||
Ctx.regDMODE = 0;
|
||||
Ctx.regDSTAT = 0x80; // DMA FIFO empty
|
||||
Ctx.regISTAT = 0;
|
||||
Ctx.halt = false;
|
||||
|
||||
DebugLog("53C810 reset\n");
|
||||
memset(Ctx.regs, 0, sizeof(Ctx.regs));
|
||||
Ctx.regs[0x00] = 0xC0; // SCNTL0
|
||||
Ctx.regs[0x0C] = 0x80; // DSTAT
|
||||
Ctx.regs[0x0F] = 0x02; // SSTAT2
|
||||
Ctx.regs[0x18] = 0xFF; // reserved
|
||||
Ctx.regs[0x19] = 0xF0; // CTEST1
|
||||
Ctx.regs[0x1A] = 0x01; // CTEST2
|
||||
Ctx.regs[0x46] = 0x60; // MACNTL
|
||||
Ctx.regs[0x47] = 0x0F; // GPCNTL
|
||||
Ctx.regs[0x4C] = 0x03; // STEST0
|
||||
Ctx.regTEMP = 0;
|
||||
Ctx.regDSP = 0;
|
||||
Ctx.regDSPS = 0;
|
||||
Ctx.regDBC = 0;
|
||||
Ctx.regDCMD = 0;
|
||||
Ctx.regDCNTL = 0;
|
||||
Ctx.regDMODE = 0;
|
||||
Ctx.regDSTAT = 0x80; // DMA FIFO empty
|
||||
Ctx.regISTAT = 0;
|
||||
Ctx.halt = false;
|
||||
|
||||
DebugLog("53C810 reset\n");
|
||||
}
|
||||
|
||||
|
||||
|
@ -484,23 +479,23 @@ void C53C810::Reset(void)
|
|||
|
||||
void C53C810::Init(CBus *BusObjectPtr, CIRQ *IRQObjectPtr, unsigned scsiIRQBit)
|
||||
{
|
||||
Ctx.Bus = BusObjectPtr;
|
||||
Ctx.IRQ = IRQObjectPtr;
|
||||
Ctx.scsiIRQ = scsiIRQBit;
|
||||
Ctx.Bus = BusObjectPtr;
|
||||
Ctx.IRQ = IRQObjectPtr;
|
||||
Ctx.scsiIRQ = scsiIRQBit;
|
||||
}
|
||||
|
||||
C53C810::C53C810(void)
|
||||
{
|
||||
BuildOpTable();
|
||||
Ctx.Bus = NULL;
|
||||
Ctx.IRQ = NULL;
|
||||
scsiIRQ = 0;
|
||||
DebugLog("Built 53C810\n");
|
||||
{
|
||||
BuildOpTable();
|
||||
Ctx.Bus = NULL;
|
||||
Ctx.IRQ = NULL;
|
||||
scsiIRQ = 0;
|
||||
DebugLog("Built 53C810\n");
|
||||
}
|
||||
|
||||
C53C810::~C53C810(void)
|
||||
{
|
||||
Ctx.Bus = NULL;
|
||||
Ctx.IRQ = NULL;
|
||||
DebugLog("Destroyed 53C810\n");
|
||||
{
|
||||
Ctx.Bus = NULL;
|
||||
Ctx.IRQ = NULL;
|
||||
DebugLog("Destroyed 53C810\n");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue