diff options
Diffstat (limited to 'src/emu/machine/am53cf96.c')
-rw-r--r-- | src/emu/machine/am53cf96.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/src/emu/machine/am53cf96.c b/src/emu/machine/am53cf96.c new file mode 100644 index 00000000000..a902508eb89 --- /dev/null +++ b/src/emu/machine/am53cf96.c @@ -0,0 +1,279 @@ +/* + * am53cf96.c + * + * AMD/NCR/Symbios 53CF96 SCSI-2 controller. + * Qlogic FAS-236 and Emulex ESP-236 are equivalents + * + * References: + * AMD Am53CF96 manual + * + */ + +#include "driver.h" +#include "am53cf96.h" + +static UINT8 scsi_regs[32], fifo[16], fptr = 0, xfer_state, last_id; +static struct AM53CF96interface *intf; + +static SCSIInstance *devices[8]; // SCSI IDs 0-7 + +// 53CF96 register set +enum +{ + REG_XFERCNTLOW = 0, // read = current xfer count lo byte, write = set xfer count lo byte + REG_XFERCNTMID, // read = current xfer count mid byte, write = set xfer count mid byte + REG_FIFO, // read/write = FIFO + REG_COMMAND, // read/write = command + + REG_STATUS, // read = status, write = destination SCSI ID (4) + REG_IRQSTATE, // read = IRQ status, write = timeout (5) + REG_INTSTATE, // read = internal state, write = sync xfer period (6) + REG_FIFOSTATE, // read = FIFO status, write = sync offset + REG_CTRL1, // read/write = control 1 + REG_CLOCKFCTR, // clock factor (write only) + REG_TESTMODE, // test mode (write only) + REG_CTRL2, // read/write = control 2 + REG_CTRL3, // read/write = control 3 + REG_CTRL4, // read/write = control 4 + REG_XFERCNTHI, // read = current xfer count hi byte, write = set xfer count hi byte + REG_DATAALIGN // data alignment (write only) +}; + +READ32_HANDLER( am53cf96_r ) +{ + int reg, shift, rv; + static const int states[] = { 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 0 }; + + reg = offset * 2; + if (mem_mask == 0xffffff00) + { + shift = 0; + } + else + { + reg++; + shift = 16; + } + + if (reg == REG_STATUS) + { + scsi_regs[REG_STATUS] &= ~0x7; + scsi_regs[REG_STATUS] |= states[xfer_state]; + if (xfer_state < 10) + { + xfer_state++; + } + } + + rv = scsi_regs[reg]<<shift; + + if (reg == REG_FIFO) + { +// mame_printf_debug("53cf96: read FIFO PC=%x\n", activecpu_get_pc()); + return 0; + } + +// logerror("53cf96: read reg %d = %x (PC=%x)\n", reg, rv>>shift, activecpu_get_pc()); + + if (reg == REG_IRQSTATE) + { + scsi_regs[REG_STATUS] &= ~0x80; // clear IRQ flag + } + + return rv; +} + +static TIMER_CALLBACK( am53cf96_irq ) +{ + scsi_regs[REG_IRQSTATE] = 8; // indicate success + scsi_regs[REG_STATUS] |= 0x80; // indicate IRQ + intf->irq_callback(); +} + +WRITE32_HANDLER( am53cf96_w ) +{ + int reg, val, dma; + + reg = offset * 2; + val = data; + if (mem_mask == 0xffffff00) + { + } + else + { + reg++; + val >>= 16; + } + val &= 0xff; + +// logerror("53cf96: w %x to reg %d (ofs %02x data %08x mask %08x PC=%x)\n", val, reg, offset, data, mem_mask, activecpu_get_pc()); + + // if writing to the target ID, cache it off for later + if (reg == REG_STATUS) + { + last_id = val; + } + + if (reg == REG_XFERCNTLOW || reg == REG_XFERCNTMID || reg == REG_XFERCNTHI) + { + scsi_regs[REG_STATUS] &= ~0x10; // clear CTZ bit + } + + // FIFO + if (reg == REG_FIFO) + { +// mame_printf_debug("%02x to FIFO @ %02d\n", val, fptr); + fifo[fptr++] = val; + if (fptr > 15) + { + fptr = 15; + } + } + + // command + if (reg == REG_COMMAND) + { + dma = (val & 0x80) ? 1 : 0; + fptr = 0; + switch (val & 0x7f) + { + case 0: // NOP + scsi_regs[REG_IRQSTATE] = 8; // indicate success + xfer_state = 0; + break; + case 2: // reset device + scsi_regs[REG_IRQSTATE] = 8; // indicate success + + logerror("53cf96: reset target ID = %d (PC = %x)\n", last_id, activecpu_get_pc()); + if (devices[last_id]) + { + SCSIReset( devices[last_id] ); + } + else + { + logerror("53cf96: reset request for unknown device SCSI ID %d\n", last_id); + } + + xfer_state = 0; + break; + case 3: // reset SCSI bus + scsi_regs[REG_INTSTATE] = 4; // command sent OK + xfer_state = 0; + timer_set( ATTOTIME_IN_HZ( 16384 ), 0, am53cf96_irq ); + break; + case 0x42: // select with ATN steps + timer_set( ATTOTIME_IN_HZ( 16384 ), 0, am53cf96_irq ); + if ((fifo[1] == 0) || (fifo[1] == 0x48) || (fifo[1] == 0x4b)) + { + scsi_regs[REG_INTSTATE] = 6; + } + else + { + scsi_regs[REG_INTSTATE] = 4; + } + + logerror("53cf96: command %x exec. target ID = %d (PC = %x)\n", fifo[1], last_id, activecpu_get_pc()); + if (devices[last_id]) + { + int length; + + SCSISetCommand( devices[last_id], &fifo[1], 12 ); + SCSIExecCommand( devices[last_id], &length ); + } + else + { + logerror("53cf96: request for unknown device SCSI ID %d\n", last_id); + } + xfer_state = 0; + break; + case 0x44: // enable selection/reselection + xfer_state = 0; + break; + case 0x10: // information transfer (must not change xfer_state) + case 0x11: // second phase of information transfer + case 0x12: // message accepted + timer_set( ATTOTIME_IN_HZ( 16384 ), 0, am53cf96_irq ); + scsi_regs[REG_INTSTATE] = 6; // command sent OK + break; + default: + printf( "unsupported command %02x\n", val ); + break; + } + } + + // only update the register mirror if it's not a write-only reg + if (reg != REG_STATUS && reg != REG_INTSTATE && reg != REG_IRQSTATE && reg != REG_FIFOSTATE) + { + scsi_regs[reg] = val; + } +} + +void am53cf96_init( struct AM53CF96interface *interface ) +{ + int i; + + // save interface pointer for later + intf = interface; + + memset(scsi_regs, 0, sizeof(scsi_regs)); + 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].diskID ); + } + + state_save_register_global_array(scsi_regs); + state_save_register_global_array(fifo); + state_save_register_global(fptr); + state_save_register_global(xfer_state); + state_save_register_global(last_id); +} + +// retrieve data from the SCSI controller +void am53cf96_read_data(int bytes, UINT8 *pData) +{ + scsi_regs[REG_STATUS] |= 0x10; // indicate DMA finished + + if (devices[last_id]) + { + SCSIReadData( devices[last_id], pData, bytes ); + } + else + { + logerror("53cf96: request for unknown device SCSI ID %d\n", last_id); + } +} + +// write data to the SCSI controller +void am53cf96_write_data(int bytes, UINT8 *pData) +{ +// int i; + + scsi_regs[REG_STATUS] |= 0x10; // indicate DMA finished + + if (devices[last_id]) + { + SCSIWriteData( devices[last_id], pData, bytes ); + } + else + { + logerror("53cf96: request for unknown device SCSI ID %d\n", last_id); + } +} + +// get the device handle (HD or CD) for the specified SCSI ID +void *am53cf96_get_device(int id) +{ + void *ret; + + if (devices[id]) + { + logerror("53cf96: fetching dev pointer for SCSI ID %d\n", id); + SCSIGetDevice( devices[id], &ret ); + return ret; + } + + return NULL; +} |