mame/src/emu/machine/53c810.c
Aaron Giles 6d5941e085 Disks opened by the romload module are no longer identified by index.
Instead, they are identified by the region they were loaded in. This
generally means one disk per region. get_disk_handle() now takes a
region name in place of an index. Updated all callers to do this.
The SCSI modules in particular posed a challenge to make this work,
so watch out for potential bugs there.

Changed the IDE interfaces to default to choosing the region with the
same tag as the device for its master disk (assuming no slave). Added
support for specifying explicit master/slave disk regions as part of
the configuration, though slave disks are still not supported yet.

Change the laserdisc interface to no longer require a disk number or
a custom sound device. Both are now assumed to be tagged the same as
the laserdisc device. Updated all drivers accordingly.
2008-08-01 23:11:08 +00:00

914 lines
18 KiB
C

/* LSI Logic LSI53C810A PCI to SCSI I/O Processor */
#include "driver.h"
#include "deprecat.h"
#include "53c810.h"
#define DMA_MAX_ICOUNT 512 /* Maximum number of DMA Scripts opcodes to run */
#define DASM_OPCODES 0
static SCSIInstance *devices[8]; /* SCSI IDs 0-7 */
static const struct LSI53C810interface *intf;
static UINT8 last_id;
static struct {
UINT8 scntl0;
UINT8 scntl1;
UINT8 scntl2;
UINT8 scntl3;
UINT8 scid;
UINT8 socl;
UINT8 istat;
UINT8 dstat;
UINT8 sstat0;
UINT8 sstat1;
UINT8 sstat2;
UINT8 dien;
UINT8 dcntl;
UINT8 dmode;
UINT32 temp;
UINT32 dsa;
UINT32 dsp;
UINT32 dsps;
UINT32 dcmd;
UINT8 sien0;
UINT8 sien1;
UINT8 stime0;
UINT8 respid;
UINT8 stest1;
UINT8 scratch_a[4];
UINT8 scratch_b[4];
int dma_icount;
int halted;
int carry;
UINT32 (* fetch)(UINT32 dsp);
void (* irq_callback)(running_machine *machine);
void (* dma_callback)(UINT32, UINT32, int, int);
} lsi810;
static void (* dma_opcode[256])(void);
INLINE UINT32 FETCH(void)
{
UINT32 r = intf->fetch(lsi810.dsp);
lsi810.dsp += 4;
return r;
}
#ifdef UNUSED_FUNCTION
static UINT32 sign_extend24(UINT32 val)
{
if (val & 0x00800000)
val |= 0xFF000000;
else
val &= ~0xFF000000;
return val;
}
#endif
static void dmaop_invalid(void)
{
fatalerror("LSI53C810: Invalid SCRIPTS DMA opcode %08X at %08X", lsi810.dcmd, lsi810.dsp);
}
static void dmaop_move_memory(void)
{
UINT32 src = FETCH();
UINT32 dst = FETCH();
int count;
count = lsi810.dcmd & 0xffffff;
if(intf->dma_callback != NULL) {
intf->dma_callback(src, dst, count, 1);
}
}
static void dmaop_interrupt(void)
{
if(lsi810.dcmd & 0x100000) {
fatalerror("LSI53C810: INTFLY opcode not implemented");
}
lsi810.dsps = FETCH();
lsi810.istat |= 0x1; /* DMA interrupt pending */
lsi810.dstat |= 0x4; /* SIR (SCRIPTS Interrupt Instruction Received) */
if(intf->irq_callback != NULL) {
intf->irq_callback(Machine, 1);
}
lsi810.dma_icount = 0;
lsi810.halted = 1;
}
static void dmaop_block_move(void)
{
UINT32 address;
UINT32 count;
INT32 dsps;
address = FETCH();
count = lsi810.dcmd & 0x00ffffff;
// normal indirect
if (lsi810.dcmd & 0x20000000)
address = intf->fetch(address);
// table indirect
if (lsi810.dcmd & 0x10000000)
{
dsps = (INT32)address&0xffffff;
// sign extend
if (dsps & 0x00800000)
{
dsps |= 0xff000000;
}
logerror("table offset: %x, DSA = %x\n", dsps, lsi810.dsa);
dsps += lsi810.dsa;
logerror("Loading from table at %x\n", dsps);
count = lsi810.fetch(dsps);
address = lsi810.fetch(dsps+4);
}
logerror("block move: address %x count %x phase %x\n", address, count, (lsi810.dcmd>>24)&7);
if (lsi810.scntl0 & 0x01)
{
/* target mode */
fatalerror("LSI53C810: dmaop_block_move not implemented in target mode");
}
else
{
/* initiator mode */
logerror("53c810: block_move not actually implemented\n");
}
}
static void dmaop_select(void)
{
UINT32 operand;
operand = FETCH();
if (lsi810.scntl0 & 0x01)
{
/* target mode */
logerror("LSI53C810: reselect ID #%d\n", (lsi810.dcmd >> 16) & 0x07);
}
else
{
/* initiator mode */
logerror("53c810: SELECT: our ID %d, target ID %d\n", lsi810.scid&7, (lsi810.dcmd>>16)&7);
lsi810.sstat1 &= ~0x07; // clear current bus phase
if (lsi810.dcmd & 0x01000000) // select with ATN
{
mame_printf_debug("53c810: want select with ATN, setting message phase\n");
lsi810.sstat1 |= 0x7; // ATN means we want message in phase
}
}
}
static void dmaop_wait_disconnect(void)
{
UINT32 operand;
operand = FETCH();
if (lsi810.scntl0 & 0x01)
{
/* target mode */
fatalerror("LSI53C810: dmaop_wait_disconnect not implemented in target mode");
}
else
{
/* initiator mode */
fatalerror("LSI53C810: dmaop_wait_disconnect not implemented");
}
}
static void dmaop_wait_reselect(void)
{
UINT32 operand;
operand = FETCH();
if (lsi810.scntl0 & 0x01)
{
/* target mode */
fatalerror("LSI53C810: dmaop_wait_reselect not implemented in target mode");
}
else
{
/* initiator mode */
fatalerror("LSI53C810: dmaop_wait_reselect not implemented");
}
}
static void dmaop_set(void)
{
UINT32 operand;
operand = FETCH();
/* initiator mode */
if (lsi810.dcmd & 0x8)
{
// set ATN in SOCL
lsi810.socl |= 0x08;
}
if (lsi810.dcmd & 0x40)
{
// set ACK in SOCL
lsi810.socl |= 0x40;
}
if (lsi810.dcmd & 0x200)
{
// set target mode
lsi810.scntl0 |= 0x01;
}
if (lsi810.dcmd & 0x400)
{
// set carry in ALU
lsi810.carry = 1;
}
}
static void dmaop_clear(void)
{
UINT32 operand;
operand = FETCH();
/* initiator mode */
if (lsi810.dcmd & 0x8)
{
// clear ATN in SOCL
lsi810.socl &= ~0x08;
}
if (lsi810.dcmd & 0x40)
{
// clear ACK in SOCL
lsi810.socl &= ~0x40;
}
if (lsi810.dcmd & 0x200)
{
// clear target mode
lsi810.scntl0 &= ~0x01;
}
if (lsi810.dcmd & 0x400)
{
// clear carry in ALU
lsi810.carry = 0;
}
}
static void dmaop_move_from_sfbr(void)
{
fatalerror("LSI53C810: dmaop_move_from_sfbr not implemented in target mode");
}
static void dmaop_move_to_sfbr(void)
{
fatalerror("LSI53C810: dmaop_move_to_sfbr not implemented");
}
static void dmaop_read_modify_write(void)
{
fatalerror("LSI53C810: dmaop_read_modify_write not implemented");
}
static int scripts_compute_branch(void)
{
int dtest, ptest, wanted, passed;
// |jump if true
// 878b0000 ||compare data
// 1000 0111 1000 1011 0000 0000 0000 0000
// | |rel ||wait valid phase
// | |compare phase
// |desired phase: message in
if (lsi810.dcmd & 0x00200000)
{
fatalerror("LSI53C810: jump with carry test not implemented");
}
if (lsi810.dcmd & 0x00100000)
{
fatalerror("LSI53C810: jump with interrupt on the fly not implemented");
}
// set desired result to take jump
wanted = (lsi810.dcmd & 0x00080000) ? 1 : 0;
// default to passing the tests in case they're disabled
dtest = ptest = wanted;
// phase test?
if (lsi810.dcmd & 0x00020000)
{
logerror("53c810: phase test. current: %x. target: %x\n", lsi810.sstat1 & 7, (lsi810.dcmd>>24)&7);
// do the phases match?
if (((lsi810.dcmd>>24)&7) == (lsi810.sstat1 & 7))
{
ptest = 1;
}
else
{
ptest = 0;
}
}
// data test?
if (lsi810.dcmd & 0x00040000)
{
logerror("53c810: data test. target: %x [not yet implemented]\n", lsi810.dcmd&0xff);
}
// if all conditions go, take the jump
passed = 0;
if ((ptest == dtest) && (dtest == wanted))
{
passed = 1;
}
logerror("53c810: phase test %d data test %d wanted %d => pass %d\n", ptest, dtest, wanted, passed);
return passed;
}
static UINT32 scripts_get_jump_dest(void)
{
INT32 dsps;
UINT32 dest;
dsps = FETCH();
/* relative or absolute addressing? */
if (lsi810.dcmd & 0x00800000)
{
// sign-extend the 24-bit value
if (dsps & 0x00800000)
{
dsps |= 0xff000000;
}
logerror("dsps = %x, dsp = %x\n", dsps, lsi810.dsp);
dsps += lsi810.dsp;
}
dest = (UINT32)dsps;
logerror("cur DSP %x, dest %x\n", lsi810.dsp, dest);
return dest;
}
static void dmaop_jump(void)
{
if (scripts_compute_branch())
{
lsi810.dsp = scripts_get_jump_dest();
}
else
{
FETCH(); // skip operand to continue on
}
}
static void dmaop_call(void)
{
if (scripts_compute_branch())
{
// save return address
lsi810.temp = lsi810.dsp;
// and go
lsi810.dsp = scripts_get_jump_dest();
}
else
{
FETCH(); // skip operand to continue on
}
}
static void dmaop_return(void)
{
// is this correct? return only happens if the condition is true?
if (scripts_compute_branch())
{
// restore return address
lsi810.dsp = lsi810.temp;
}
else
{
FETCH(); // skip operand to continue on
}
}
static void dmaop_store(void)
{
fatalerror("LSI53C810: dmaop_store not implemented");
}
static void dmaop_load(void)
{
fatalerror("LSI53C810: dmaop_load not implemented");
}
static void dma_exec(void)
{
lsi810.dma_icount = DMA_MAX_ICOUNT;
while(lsi810.dma_icount > 0)
{
int op;
if (DASM_OPCODES)
{
char buf[256];
lsi53c810_dasm(buf, lsi810.dsp);
logerror("0x%08X: %s\n", lsi810.dsp, buf);
}
lsi810.dcmd = FETCH();
op = (lsi810.dcmd >> 24) & 0xff;
dma_opcode[op]();
lsi810.dma_icount--;
}
}
READ8_HANDLER( lsi53c810_reg_r )
{
logerror("53c810: read reg %d:0x%x (PC=%x)\n", offset, offset, activecpu_get_pc());
switch(offset)
{
case 0x00: /* SCNTL0 */
return lsi810.scntl0;
case 0x01: /* SCNTL1 */
return lsi810.scntl1;
case 0x02: /* SCNTL2 */
return lsi810.scntl2;
case 0x03: /* SCNTL3 */
return lsi810.scntl3;
case 0x04: /* SCID */
return lsi810.scid;
case 0x09: /* SOCL */
return lsi810.socl;
case 0x0c: /* DSTAT */
return lsi810.dstat;
case 0x0d: /* SSTAT0 */
return lsi810.sstat0;
case 0x0e: /* SSTAT1 */
return lsi810.sstat1;
case 0x0f: /* SSTAT2 */
return lsi810.sstat2;
case 0x10: /* DSA [7-0] */
return lsi810.dsa & 0xff;
case 0x11: /* DSA [15-8] */
return (lsi810.dsa >> 8) & 0xff;
case 0x12: /* DSA [23-16] */
return (lsi810.dsa >> 16) & 0xff;
case 0x13: /* DSA [31-24] */
return (lsi810.dsa >> 24) & 0xff;
case 0x14: /* ISTAT */
// clear the interrupt on service
if(intf->irq_callback != NULL)
{
intf->irq_callback(machine, 0);
}
return lsi810.istat;
case 0x2c: /* DSP [7-0] */
return lsi810.dsp & 0xff;
case 0x2d: /* DSP [15-8] */
return (lsi810.dsp >> 8) & 0xff;
case 0x2e: /* DSP [23-16] */
return (lsi810.dsp >> 16) & 0xff;
case 0x2f: /* DSP [31-24] */
return (lsi810.dsp >> 24) & 0xff;
case 0x34: /* SCRATCH A */
case 0x35:
case 0x36:
case 0x37:
return lsi810.scratch_a[offset % 4];
case 0x39: /* DIEN */
return lsi810.dien;
case 0x3b: /* DCNTL */
return lsi810.dcntl;
case 0x40: /* SIEN0 */
return lsi810.sien0;
case 0x41: /* SIEN1 */
return lsi810.sien1;
case 0x48: /* STIME0 */
return lsi810.stime0;
case 0x4a: /* RESPID */
return lsi810.respid;
case 0x4d: /* STEST1 */
return lsi810.stest1;
case 0x5c: /* SCRATCH B */
case 0x5d:
case 0x5e:
case 0x5f:
return lsi810.scratch_b[offset % 4];
default:
fatalerror("LSI53C810: reg_r: Unknown reg %02X", offset);
}
return 0;
}
WRITE8_HANDLER( lsi53c810_reg_w )
{
logerror("53c810: %02x to reg %d:0x%x (PC=%x)\n", data, offset, offset, activecpu_get_pc());
switch(offset)
{
case 0x00: /* SCNTL0 */
lsi810.scntl0 = data;
break;
case 0x01: /* SCNTL1 */
lsi810.scntl1 = data;
break;
case 0x02: /* SCNTL2 */
lsi810.scntl2 = data;
break;
case 0x03: /* SCNTL3 */
lsi810.scntl3 = data;
break;
case 0x04: /* SCID */
lsi810.scid = data;
break;
case 0x09: /* SOCL */
lsi810.socl = data;
break;
case 0x0d: /* SSTAT0 */
lsi810.sstat0 = data;
break;
case 0x0e: /* SSTAT1 */
lsi810.sstat1 = data;
break;
case 0x0f: /* SSTAT2 */
lsi810.sstat2 = data;
break;
case 0x10: /* DSA [7-0] */
lsi810.dsa &= 0xffffff00;
lsi810.dsa |= data;
break;
case 0x11: /* DSA [15-8] */
lsi810.dsa &= 0xffff00ff;
lsi810.dsa |= data << 8;
break;
case 0x12: /* DSA [23-16] */
lsi810.dsa &= 0xff00ffff;
lsi810.dsa |= data << 16;
break;
case 0x13: /* DSA [31-24] */
lsi810.dsa &= 0x00ffffff;
lsi810.dsa |= data << 24;
break;
case 0x14: /* ISTAT */
lsi810.istat = data;
break;
case 0x2c: /* DSP [7-0] */
lsi810.dsp &= 0xffffff00;
lsi810.dsp |= data;
break;
case 0x2d: /* DSP [15-8] */
lsi810.dsp &= 0xffff00ff;
lsi810.dsp |= data << 8;
break;
case 0x2e: /* DSP [23-16] */
lsi810.dsp &= 0xff00ffff;
lsi810.dsp |= data << 16;
break;
case 0x2f: /* DSP [31-24] */
lsi810.dsp &= 0x00ffffff;
lsi810.dsp |= data << 24;
lsi810.halted = 0;
if((lsi810.dmode & 0x1) == 0 && !lsi810.halted) {
dma_exec();
}
break;
case 0x34: /* SCRATCH A */
case 0x35:
case 0x36:
case 0x37:
lsi810.scratch_a[offset % 4] = data;
break;
case 0x38: /* DMODE */
lsi810.dmode = data;
break;
case 0x39: /* DIEN */
lsi810.dien = data;
break;
case 0x3b: /* DCNTL */
lsi810.dcntl = data;
if(lsi810.dcntl & 0x14 && !lsi810.halted) /* single-step & start DMA */
{
int op;
lsi810.dcmd = FETCH();
op = (lsi810.dcmd >> 24) & 0xff;
dma_opcode[op]();
lsi810.istat |= 0x3; /* DMA interrupt pending */
lsi810.dstat |= 0x8; /* SSI (Single Step Interrupt) */
if(intf->irq_callback != NULL) {
intf->irq_callback(machine, 1);
}
}
else if(lsi810.dcntl & 0x04 && !lsi810.halted) /* manual start DMA */
{
dma_exec();
}
break;
case 0x40: /* SIEN0 */
lsi810.sien0 = data;
break;
case 0x41: /* SIEN1 */
lsi810.sien1 = data;
break;
case 0x48: /* STIME0 */
lsi810.stime0 = data;
break;
case 0x4a: /* RESPID */
lsi810.respid = data;
break;
case 0x4d: /* STEST1 */
lsi810.stest1 = data;
break;
case 0x5c: /* SCRATCH B */
case 0x5d:
case 0x5e:
case 0x5f:
lsi810.scratch_b[offset % 4] = data;
break;
default:
fatalerror("LSI53C810: reg_w: Unknown reg %02X, %02X", offset, data);
}
}
static void add_opcode(UINT8 op, UINT8 mask, void (* handler)(void))
{
int i;
for(i=0; i < 256; i++) {
if((i & mask) == op) {
dma_opcode[i] = handler;
}
}
}
extern void lsi53c810_init(const struct LSI53C810interface *interface)
{
int i;
// save interface pointer for later
intf = interface;
memset(&lsi810, 0, sizeof(lsi810));
for(i = 0; i < 256; i++)
{
dma_opcode[i] = dmaop_invalid;
}
add_opcode(0x00, 0xc0, dmaop_block_move);
add_opcode(0x40, 0xf8, dmaop_select);
add_opcode(0x48, 0xf8, dmaop_wait_disconnect);
add_opcode(0x50, 0xf8, dmaop_wait_reselect);
add_opcode(0x58, 0xf8, dmaop_set);
add_opcode(0x60, 0xf8, dmaop_clear);
add_opcode(0x68, 0xf8, dmaop_move_from_sfbr);
add_opcode(0x70, 0xf8, dmaop_move_to_sfbr);
add_opcode(0x78, 0xf8, dmaop_read_modify_write);
add_opcode(0x80, 0xf8, dmaop_jump);
add_opcode(0x88, 0xf8, dmaop_call);
add_opcode(0x90, 0xf8, dmaop_return);
add_opcode(0x98, 0xf8, dmaop_interrupt);
add_opcode(0xc0, 0xfe, dmaop_move_memory);
add_opcode(0xe0, 0xed, dmaop_store);
add_opcode(0xe1, 0xed, dmaop_load);
memset(devices, 0, sizeof(devices));
// try to open the devices
for (i = 0; i < interface->scsidevs->devs_present; i++)
{
SCSIAllocInstance( interface->scsidevs->devices[i].scsiClass, &devices[interface->scsidevs->devices[i].scsiID], interface->scsidevs->devices[i].diskregion );
}
}
extern void lsi53c810_exit(const struct LSI53C810interface *interface)
{
int i;
for (i = 0; i < interface->scsidevs->devs_present; i++)
{
SCSIDeleteInstance( devices[interface->scsidevs->devices[i].scsiID] );
}
}
void lsi53c810_read_data(int bytes, UINT8 *pData)
{
if (devices[last_id])
{
SCSIReadData( devices[last_id], pData, bytes);
}
else
{
logerror("lsi53c810: read unknown device SCSI ID %d\n", last_id);
}
}
void lsi53c810_write_data(int bytes, UINT8 *pData)
{
if (devices[last_id])
{
SCSIWriteData( devices[last_id], pData, bytes );
}
else
{
logerror("lsi53c810: write to unknown device SCSI ID %d\n", last_id);
}
}
void *lsi53c810_get_device(int id)
{
void *ret;
if (devices[id])
{
logerror("lsi53c810: fetching dev pointer for SCSI ID %d\n", id);
SCSIGetDevice( devices[id], &ret );
return ret;
}
return NULL;
}
/*************************************
*
* Disassembler
*
*************************************/
static UINT32 lsi53c810_dasm_fetch(UINT32 pc)
{
return intf->fetch(pc);
}
unsigned lsi53c810_dasm(char *buf, UINT32 pc)
{
unsigned result = 0;
const char *op_mnemonic = NULL;
UINT32 op = lsi53c810_dasm_fetch(pc);
UINT32 dest;
int i;
static const char *const phases[] =
{
"DATA_OUT", "DATA_IN", "CMD", "STATUS",
"RESERVED_OUT??", "RESERVED_IN??", "MSG_OUT", "MSG_IN"
};
if ((op & 0xF8000000) == 0x40000000)
{
/* SELECT */
dest = lsi53c810_dasm_fetch(pc + 4);
buf += sprintf(buf, "SELECT%s %d, 0x%08X",
(op & 0x01000000) ? " ATN" : "",
(op >> 16) & 0x07,
dest);
result = 8;
}
else if (((op & 0xF8000000) == 0x58000000)
| ((op & 0xF8000000) == 0x60000000))
{
static const struct
{
UINT32 flag;
const char *text;
} flags[] =
{
{ 0x00000008, "ATN" },
{ 0x00000040, "ACK" },
{ 0x00000200, "TARGET" },
{ 0x00000400, "CARRY" }
};
int need_cojunction = FALSE;
/* SET/CLEAR */
switch(op & 0xF8000000)
{
case 0x58000000: op_mnemonic = "SET"; break;
case 0x60000000: op_mnemonic = "CLEAR"; break;
}
buf += sprintf(buf, "%s ", op_mnemonic);
need_cojunction = FALSE;
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++)
{
if (op & flags[i].flag)
{
if (need_cojunction)
buf += sprintf(buf, " AND ");
else
need_cojunction = TRUE;
buf += sprintf(buf, "%s", flags[i].text);
}
}
}
else if (((op & 0xF8000000) == 0x80000000)
| ((op & 0xF8000000) == 0x88000000)
| ((op & 0xF8000000) == 0x98000000))
{
/* JUMP/CALL/INT */
switch(op & 0xF8000000)
{
case 0x80000000: op_mnemonic = "JUMP"; break;
case 0x88000000: op_mnemonic = "CALL"; break;
case 0x98000000: op_mnemonic = "INT"; break;
}
dest = lsi53c810_dasm_fetch(pc + 4);
if (op & 0x00800000)
{
/* relative */
if (dest & 0x00800000)
dest |= 0xFF000000;
else
dest &= 0x00FFFFFF;
dest = (pc + 8) + dest;
buf += sprintf(buf, "%s REL(0x%08X)", op_mnemonic, dest);
}
else
{
/* absolute */
buf += sprintf(buf, "%s 0x%08X", op_mnemonic, dest);
}
switch(op & 0x000B0000)
{
case 0x00000000:
buf += sprintf(buf, ", NOT??");
break;
case 0x00080000:
break;
case 0x00020000:
case 0x00030000:
case 0x000A0000:
case 0x000B0000:
buf += sprintf(buf, ", %s%s %s",
(op & 0x00010000) ? "WHEN" : "IF",
(op & 0x00080000) ? "" : " NOT",
phases[(op >> 24) & 0x07]);
break;
default:
fatalerror("unknown op 0x%08X", op);
break;
}
result = 8;
}
else if ((op & 0xE0000000) == 0x00000000)
{
/* MOVE FROM */
dest = lsi53c810_dasm_fetch(pc + 4);
buf += sprintf(buf, "MOVE FROM 0x%08X, WHEN %s",
dest, phases[(op >> 24) & 0x07]);
result = 8;
}
else if ((op & 0xE0000000) == 0x20000000)
{
/* MOVE PTR */
dest = lsi53c810_dasm_fetch(pc + 4);
buf += sprintf(buf, "MOVE 0x%08X, PTR 0x%08X, WHEN %s",
(op & 0x00FFFFFF), dest, phases[(op >> 24) & 0x07]);
result = 8;
}
else
{
fatalerror("unknown op 0x%08X", op);
}
return result;
}