diff options
Diffstat (limited to 'trunk/src/emu/machine/wd33c93.c')
-rw-r--r-- | trunk/src/emu/machine/wd33c93.c | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/wd33c93.c b/trunk/src/emu/machine/wd33c93.c new file mode 100644 index 00000000000..4beca1d0780 --- /dev/null +++ b/trunk/src/emu/machine/wd33c93.c @@ -0,0 +1,877 @@ +/* + * wd33c93.c + * + * WD/AMD 33c93 SCSI controller, as seen in + * early PCs, some MSX add-ons, NEC PC-88, and SGI + * Indigo, Indigo2, and Indy systems. + * + * References: + * WD 33c93 manual + * NetBSD 33c93 driver + * + */ + +#include "emu.h" +#include "wd33c93.h" + +#define VERBOSE 0 +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + +static SCSIInstance *devices[8]; // SCSI IDs 0-7 +static const struct WD33C93interface *intf; + +/* wd register names */ +#define WD_OWN_ID 0x00 +#define WD_CONTROL 0x01 +#define WD_TIMEOUT_PERIOD 0x02 +#define WD_CDB_1 0x03 +#define WD_CDB_2 0x04 +#define WD_CDB_3 0x05 +#define WD_CDB_4 0x06 +#define WD_CDB_5 0x07 +#define WD_CDB_6 0x08 +#define WD_CDB_7 0x09 +#define WD_CDB_8 0x0a +#define WD_CDB_9 0x0b +#define WD_CDB_10 0x0c +#define WD_CDB_11 0x0d +#define WD_CDB_12 0x0e +#define WD_TARGET_LUN 0x0f +#define WD_COMMAND_PHASE 0x10 +#define WD_SYNCHRONOUS_TRANSFER 0x11 +#define WD_TRANSFER_COUNT_MSB 0x12 +#define WD_TRANSFER_COUNT 0x13 +#define WD_TRANSFER_COUNT_LSB 0x14 +#define WD_DESTINATION_ID 0x15 +#define WD_SOURCE_ID 0x16 +#define WD_SCSI_STATUS 0x17 +#define WD_COMMAND 0x18 +#define WD_DATA 0x19 +#define WD_QUEUE_TAG 0x1a +#define WD_AUXILIARY_STATUS 0x1f + +/* WD commands */ +#define WD_CMD_RESET 0x00 +#define WD_CMD_ABORT 0x01 +#define WD_CMD_ASSERT_ATN 0x02 +#define WD_CMD_NEGATE_ACK 0x03 +#define WD_CMD_DISCONNECT 0x04 +#define WD_CMD_RESELECT 0x05 +#define WD_CMD_SEL_ATN 0x06 +#define WD_CMD_SEL 0x07 +#define WD_CMD_SEL_ATN_XFER 0x08 +#define WD_CMD_SEL_XFER 0x09 +#define WD_CMD_RESEL_RECEIVE 0x0a +#define WD_CMD_RESEL_SEND 0x0b +#define WD_CMD_WAIT_SEL_RECEIVE 0x0c +#define WD_CMD_SSCC 0x0d +#define WD_CMD_SND_DISC 0x0e +#define WD_CMD_SET_IDI 0x0f +#define WD_CMD_RCV_CMD 0x10 +#define WD_CMD_RCV_DATA 0x11 +#define WD_CMD_RCV_MSG_OUT 0x12 +#define WD_CMD_RCV 0x13 +#define WD_CMD_SND_STATUS 0x14 +#define WD_CMD_SND_DATA 0x15 +#define WD_CMD_SND_MSG_IN 0x16 +#define WD_CMD_SND 0x17 +#define WD_CMD_TRANS_ADDR 0x18 +#define WD_CMD_XFER_PAD 0x19 +#define WD_CMD_TRANS_INFO 0x20 +#define WD_CMD_TRANSFER_PAD 0x21 +#define WD_CMD_SBT_MODE 0x80 + +/* ASR register */ +#define ASR_INT 0x80 +#define ASR_LCI 0x40 +#define ASR_BSY 0x20 +#define ASR_CIP 0x10 +#define ASR_PE 0x02 +#define ASR_DBR 0x01 + +/* SCSI Bus Phases */ +#define PHS_DATA_OUT 0x00 +#define PHS_DATA_IN 0x01 +#define PHS_COMMAND 0x02 +#define PHS_STATUS 0x03 +#define PHS_MESS_OUT 0x06 +#define PHS_MESS_IN 0x07 + +/* Command Status Register definitions */ + + /* reset state interrupts */ +#define CSR_RESET 0x00 +#define CSR_RESET_AF 0x01 + + /* successful completion interrupts */ +#define CSR_RESELECT 0x10 +#define CSR_SELECT 0x11 +#define CSR_SEL_XFER_DONE 0x16 +#define CSR_XFER_DONE 0x18 + + /* paused or aborted interrupts */ +#define CSR_MSGIN 0x20 +#define CSR_SDP 0x21 +#define CSR_SEL_ABORT 0x22 +#define CSR_RESEL_ABORT 0x25 +#define CSR_RESEL_ABORT_AM 0x27 +#define CSR_ABORT 0x28 + + /* terminated interrupts */ +#define CSR_INVALID 0x40 +#define CSR_UNEXP_DISC 0x41 +#define CSR_TIMEOUT 0x42 +#define CSR_PARITY 0x43 +#define CSR_PARITY_ATN 0x44 +#define CSR_BAD_STATUS 0x45 +#define CSR_UNEXP 0x48 + + /* service required interrupts */ +#define CSR_RESEL 0x80 +#define CSR_RESEL_AM 0x81 +#define CSR_DISC 0x85 +#define CSR_SRV_REQ 0x88 + + /* Own ID/CDB Size register */ +#define OWNID_EAF 0x08 +#define OWNID_EHP 0x10 +#define OWNID_RAF 0x20 +#define OWNID_FS_8 0x00 +#define OWNID_FS_12 0x40 +#define OWNID_FS_16 0x80 + + /* Control register */ +#define CTRL_HSP 0x01 +#define CTRL_HA 0x02 +#define CTRL_IDI 0x04 +#define CTRL_EDI 0x08 +#define CTRL_HHP 0x10 +#define CTRL_POLLED 0x00 +#define CTRL_BURST 0x20 +#define CTRL_BUS 0x40 +#define CTRL_DMA 0x80 + + /* Synchronous Transfer Register */ +#define STR_FSS 0x80 + + /* Destination ID register */ +#define DSTID_DPD 0x40 +#define DATA_OUT_DIR 0 +#define DATA_IN_DIR 1 +#define DSTID_SCC 0x80 + + /* Source ID register */ +#define SRCID_MASK 0x07 +#define SRCID_SIV 0x08 +#define SRCID_DSP 0x20 +#define SRCID_ES 0x40 +#define SRCID_ER 0x80 + +/* command handler definition */ +typedef void (*cmd_handler)(running_machine &machine); +#define CMD_HANDLER(name) void name(running_machine &machine) + +#define TEMP_INPUT_LEN 262144 +#define FIFO_SIZE 12 + +/* internal controller data definition */ +typedef struct +{ + UINT8 sasr; + UINT8 regs[WD_AUXILIARY_STATUS+1]; + UINT8 fifo[FIFO_SIZE]; + int fifo_pos; + UINT8 *temp_input; + int temp_input_pos; + UINT8 busphase; + UINT8 identify; + int read_pending; + emu_timer *cmd_timer; +} _wd33c93_data; + +/* local instance of controller data */ +static _wd33c93_data scsi_data; + + +/* convernience functions */ +static UINT8 wd33c93_getunit( void ) +{ + /* return the destination unit id */ + return scsi_data.regs[WD_DESTINATION_ID] & SRCID_MASK; +} + +static void wd33c93_set_xfer_count( int count ) +{ + /* set the count */ + scsi_data.regs[ WD_TRANSFER_COUNT_LSB ] = count & 0xff; + scsi_data.regs[ WD_TRANSFER_COUNT ] = ( count >> 8 ) & 0xff; + scsi_data.regs[ WD_TRANSFER_COUNT_MSB ] = ( count >> 16 ) & 0xff; +} + +static int wd33c93_get_xfer_count( void ) +{ + /* get the count */ + int count = scsi_data.regs[ WD_TRANSFER_COUNT_MSB ]; + + count <<= 8; + count |= scsi_data.regs[ WD_TRANSFER_COUNT ]; + count <<= 8; + count |= scsi_data.regs[ WD_TRANSFER_COUNT_LSB ]; + + return count; +} + +static void wd33c93_read_data(int bytes, UINT8 *pData) +{ + UINT8 unit = wd33c93_getunit(); + + if ( devices[unit] ) + { + SCSIReadData( devices[unit], pData, bytes ); + } + else + { + logerror("wd33c93: request for unknown device SCSI ID %d\n", unit); + } +} + +static void wd33c93_complete_immediate( running_machine &machine, int status ) +{ + /* reset our timer */ + scsi_data.cmd_timer->reset( ); + + /* set the new status */ + scsi_data.regs[WD_SCSI_STATUS] = status & 0xff; + + /* set interrupt pending */ + scsi_data.regs[WD_AUXILIARY_STATUS] |= ASR_INT; + + /* check for error conditions */ + if ( wd33c93_get_xfer_count() > 0 ) + { + /* set data buffer ready */ + scsi_data.regs[WD_AUXILIARY_STATUS] |= ASR_DBR; + } + else + { + /* clear data buffer ready */ + scsi_data.regs[WD_AUXILIARY_STATUS] &= ~ASR_DBR; + } + + /* clear command in progress and bus busy */ + scsi_data.regs[WD_AUXILIARY_STATUS] &= ~(ASR_CIP | ASR_BSY); + + /* if we have a callback, call it */ + if (intf && intf->irq_callback) + { + intf->irq_callback(machine, 1); + } +} + +static TIMER_CALLBACK(wd33c93_complete_cb) +{ + wd33c93_complete_immediate( machine, param ); +} + +static TIMER_CALLBACK(wd33c93_service_request) +{ + /* issue a message out request */ + wd33c93_complete_immediate(machine, CSR_SRV_REQ | scsi_data.busphase); +} + +static TIMER_CALLBACK(wd33c93_deassert_cip) +{ + scsi_data.regs[WD_AUXILIARY_STATUS] &= ~ASR_CIP; +} + +static void wd33c93_complete_cmd( UINT8 status ) +{ + /* fire off a timer to complete the command */ + scsi_data.cmd_timer->adjust( attotime::from_usec(1), status ); +} + +/* command handlers */ +static CMD_HANDLER( wd33c93_invalid_cmd ) +{ + logerror( "%s:Unknown/Unimplemented SCSI controller command: %02x\n", machine.describe_context(), scsi_data.regs[WD_COMMAND] ); + + /* complete the command */ + wd33c93_complete_cmd( CSR_INVALID ); +} + +static CMD_HANDLER( wd33c93_reset_cmd ) +{ + int advanced = 0; + + /* see if it wants us to reset with advanced features */ + if ( scsi_data.regs[WD_OWN_ID] & OWNID_EAF ) + { + advanced = 1; + } + + /* clear out all registers */ + memset( scsi_data.regs, 0, sizeof( scsi_data.regs ) ); + + /* complete the command */ + wd33c93_complete_cmd(advanced ? CSR_RESET_AF : CSR_RESET); +} + +static CMD_HANDLER( wd33c93_abort_cmd ) +{ + /* complete the command */ + wd33c93_complete_cmd(CSR_ABORT); +} + +static CMD_HANDLER( wd33c93_disconnect_cmd ) +{ + /* complete the command */ + scsi_data.regs[WD_AUXILIARY_STATUS] &= ~(ASR_CIP | ASR_BSY); +} + +static CMD_HANDLER( wd33c93_select_cmd ) +{ + UINT8 unit = wd33c93_getunit(); + UINT8 newstatus; + + /* see if we can select that device */ + if ( devices[unit] ) + { + /* device is available - signal selection done */ + newstatus = CSR_SELECT; + + /* determine the next bus phase depending on the command */ + if ( (scsi_data.regs[WD_COMMAND] & 0x7f) == WD_CMD_SEL_ATN ) + { + /* /ATN asserted during select: Move to Message Out Phase to read identify */ + scsi_data.busphase = PHS_MESS_OUT; + } + else + { + /* No /ATN asserted: Move to Command Phase */ + scsi_data.busphase = PHS_COMMAND; + } + + /* queue up a service request out in the future */ + machine.scheduler().timer_set( attotime::from_usec(50), FUNC(wd33c93_service_request )); + } + else + { + /* device is not available */ + newstatus = CSR_TIMEOUT; + } + + /* complete the command */ + wd33c93_complete_cmd(newstatus); +} + +static CMD_HANDLER( wd33c93_selectxfer_cmd ) +{ + UINT8 unit = wd33c93_getunit(); + UINT8 newstatus; + + /* see if we can select that device */ + if ( devices[unit] ) + { + if ( scsi_data.regs[WD_COMMAND_PHASE] < 0x45 ) + { + /* device is available */ + int xfercount; + int phase; + + /* do the request */ + SCSISetCommand( devices[unit], &scsi_data.regs[WD_CDB_1], 12 ); + SCSIExecCommand( devices[unit], &xfercount ); + SCSIGetPhase( devices[unit], &phase ); + + /* set transfer count */ + if ( wd33c93_get_xfer_count() > TEMP_INPUT_LEN ) + { + logerror( "WD33C93: Transfer count too big. Please increase TEMP_INPUT_LEN (size=%d)\n", wd33c93_get_xfer_count() ); + wd33c93_set_xfer_count( TEMP_INPUT_LEN ); + } + + switch( phase ) + { + case SCSI_PHASE_DATAIN: + scsi_data.read_pending = 1; + break; + } + } + + if ( scsi_data.read_pending ) + { + int len = TEMP_INPUT_LEN; + + if ( wd33c93_get_xfer_count() < len ) len = wd33c93_get_xfer_count(); + + memset( &scsi_data.temp_input[0], 0, TEMP_INPUT_LEN ); + wd33c93_read_data( len, &scsi_data.temp_input[0] ); + scsi_data.temp_input_pos = 0; + scsi_data.read_pending = 0; + } + + scsi_data.regs[WD_TARGET_LUN] = 0; + scsi_data.regs[WD_CONTROL] |= CTRL_EDI; + scsi_data.regs[WD_COMMAND_PHASE] = 0x60; + + /* signal transfer ready */ + newstatus = CSR_SEL_XFER_DONE; + + /* if allowed disconnect, queue a service request */ + if ( scsi_data.identify & 0x40 ) + { + /* queue disconnect message in */ + scsi_data.busphase = PHS_MESS_IN; + + /* queue up a service request out in the future */ + machine.scheduler().timer_set( attotime::from_msec(50), FUNC(wd33c93_service_request )); + } + } + else + { + /* device is not available */ + newstatus = CSR_TIMEOUT; + + wd33c93_set_xfer_count( 0 ); + } + + /* complete the command */ + wd33c93_complete_cmd(newstatus); +} + +static CMD_HANDLER( wd33c93_negate_ack ) +{ + logerror( "WD33C93: ACK Negated\n" ); + + /* complete the command */ + scsi_data.regs[WD_AUXILIARY_STATUS] &= ~(ASR_CIP | ASR_BSY); +} + +static CMD_HANDLER( wd33c93_xferinfo_cmd ) +{ + /* make the buffer available right away */ + scsi_data.regs[WD_AUXILIARY_STATUS] |= ASR_DBR; + scsi_data.regs[WD_AUXILIARY_STATUS] |= ASR_CIP; + + /* the command will be completed once the data is transferred */ + machine.scheduler().timer_set( attotime::from_msec(1), FUNC(wd33c93_deassert_cip )); +} + +/* Command handlers */ +static const cmd_handler wd33c93_cmds[0x22] = +{ + &wd33c93_reset_cmd, /* 0x00 - WD_CMD_RESET */ + &wd33c93_abort_cmd, /* 0x01 - WD_CMD_ABORT */ + &wd33c93_invalid_cmd, /* 0x02 - WD_CMD_ASSERT_ATN (uninmplemented) */ + &wd33c93_negate_ack, /* 0x03 - WD_CMD_NEGATE_ACK */ + &wd33c93_disconnect_cmd,/* 0x04 - WD_CMD_DISCONNECT */ + &wd33c93_invalid_cmd, /* 0x05 - WD_CMD_RESELECT (uninmplemented) */ + &wd33c93_select_cmd, /* 0x06 - WD_CMD_SEL_ATN */ + &wd33c93_select_cmd, /* 0x07 - WD_CMD_SEL */ + &wd33c93_selectxfer_cmd,/* 0x08 - WD_CMD_SEL_ATN_XFER */ + &wd33c93_selectxfer_cmd,/* 0x09 - WD_CMD_SEL_XFER */ + &wd33c93_invalid_cmd, /* 0x0a - WD_CMD_RESEL_RECEIVE (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x0b - WD_CMD_RESEL_SEND (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x0c - WD_CMD_WAIT_SEL_RECEIVE (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x0d - WD_CMD_SSCC (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x0e - WD_CMD_SND_DISC (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x0f - WD_CMD_SET_IDI (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x10 - WD_CMD_RCV_CMD (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x11 - WD_CMD_RCV_DATA (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x12 - WD_CMD_RCV_MSG_OUT (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x13 - WD_CMD_RCV (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x14 - WD_CMD_SND_STATUS (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x15 - WD_CMD_SND_DATA (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x16 - WD_CMD_SND_MSG_IN (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x17 - WD_CMD_SND (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x18 - WD_CMD_TRANS_ADDR (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x19 - WD_CMD_XFER_PAD (uninmplemented) */ + &wd33c93_invalid_cmd, /* 0x1a - invalid */ + &wd33c93_invalid_cmd, /* 0x1b - invalid */ + &wd33c93_invalid_cmd, /* 0x1c - invalid */ + &wd33c93_invalid_cmd, /* 0x1d - invalid */ + &wd33c93_invalid_cmd, /* 0x1e - invalid */ + &wd33c93_invalid_cmd, /* 0x1f - invalid */ + &wd33c93_xferinfo_cmd, /* 0x20 - WD_CMD_TRANS_INFO) */ + &wd33c93_invalid_cmd /* 0x21 - WD_CMD_TRANSFER_PAD (uninmplemented) */ +}; + +/* Handle pending commands */ +static void wd33c93_command( running_machine &machine ) +{ + /* get the command */ + UINT8 cmd = scsi_data.regs[WD_COMMAND]; + + /* check if its within valid bounds */ + if ( (cmd & 0x7F) > WD_CMD_TRANSFER_PAD ) + { + wd33c93_invalid_cmd(machine); + return; + } + + /* call the command handler */ + (*wd33c93_cmds[cmd & 0x7F])(machine); +} + +WRITE8_HANDLER(wd33c93_w) +{ + switch( offset ) + { + case 0: + { + /* update register select */ + scsi_data.sasr = data & 0x1f; + } + break; + + case 1: + { + LOG(( "WD33C93: PC=%08x - Write REG=%02x, data = %02x\n", cpu_get_pc(&space->device()), scsi_data.sasr, data )); + + /* update the register */ + scsi_data.regs[scsi_data.sasr] = data; + + /* if we receive a command, schedule to process it */ + if ( scsi_data.sasr == WD_COMMAND ) + { + LOG(( "WDC33C93: PC=%08x - Executing command %08x - unit %d\n", cpu_get_pc(&space->device()), data, wd33c93_getunit() )); + + /* signal we're processing it */ + scsi_data.regs[WD_AUXILIARY_STATUS] |= ASR_CIP; + + /* process the command */ + wd33c93_command(space->machine()); + } + else if ( scsi_data.sasr == WD_CDB_1 ) + { + scsi_data.regs[WD_COMMAND_PHASE] = 0; + } + else if ( scsi_data.sasr == WD_DATA ) + { + /* if data was written, and we have a count, send to device */ + int count = wd33c93_get_xfer_count(); + + if ( scsi_data.regs[WD_COMMAND] & 0x80 ) + count = 1; + + if ( count-- > 0 ) + { + /* write to FIFO */ + if ( scsi_data.fifo_pos < FIFO_SIZE ) + { + scsi_data.fifo[scsi_data.fifo_pos++] = data; + } + + /* update count */ + wd33c93_set_xfer_count( count ); + + /* if we're done with the write, see where we're at */ + if ( count == 0 ) + { + scsi_data.regs[WD_AUXILIARY_STATUS] |= ASR_INT; + scsi_data.regs[WD_AUXILIARY_STATUS] &= ~ASR_DBR; + + switch( scsi_data.busphase ) + { + case PHS_MESS_OUT: + { + /* reset fifo */ + scsi_data.fifo_pos = 0; + + /* Message out phase. Data is probably SCSI Identify. Move to command phase. */ + scsi_data.busphase = PHS_COMMAND; + + scsi_data.identify = scsi_data.fifo[0]; + } + break; + + case PHS_COMMAND: + { + UINT8 unit = wd33c93_getunit(); + int xfercount; + int phase; + + /* Execute the command. Depending on the command, we'll move to data in or out */ + SCSISetCommand( devices[unit], &scsi_data.fifo[0], 12 ); + SCSIExecCommand( devices[unit], &xfercount ); + SCSIGetPhase( devices[unit], &phase ); + + /* reset fifo */ + scsi_data.fifo_pos = 0; + + /* set the new count */ + wd33c93_set_xfer_count( xfercount ); + + switch( phase ) + { + case SCSI_PHASE_STATUS: + scsi_data.busphase = PHS_STATUS; + break; + + case SCSI_PHASE_DATAIN: + scsi_data.busphase = PHS_DATA_IN; + scsi_data.read_pending = 1; + break; + + case SCSI_PHASE_DATAOUT: + scsi_data.busphase = PHS_DATA_OUT; + break; + } + } + break; + + case PHS_DATA_OUT: + { + /* write data out to device */ + wd33c93_write_data( scsi_data.fifo_pos, scsi_data.fifo ); + + /* reset fifo */ + scsi_data.fifo_pos = 0; + + /* move to status phase */ + scsi_data.busphase = PHS_STATUS; + } + break; + } + + /* complete the command */ + wd33c93_complete_immediate(space->machine(), CSR_XFER_DONE | scsi_data.busphase); + } + } + else + { + logerror( "WD33C93: Sending data to device with transfer count = 0!. Ignoring...\n" ); + } + } + + /* auto-increment register select if not on special registers */ + if ( scsi_data.sasr != WD_COMMAND && scsi_data.sasr != WD_DATA && scsi_data.sasr != WD_AUXILIARY_STATUS ) + { + scsi_data.sasr = ( scsi_data.sasr + 1 ) & 0x1f; + } + } + break; + + default: + { + logerror( "WD33C93: Write to invalid offset %d (data=%02x)\n", offset, data ); + } + break; + } +} + +READ8_HANDLER(wd33c93_r) +{ + switch( offset ) + { + case 0: + { + /* read aux status */ + return scsi_data.regs[WD_AUXILIARY_STATUS]; + } + break; + + case 1: + { + UINT8 ret; + + /* if reading status, clear irq flag */ + if ( scsi_data.sasr == WD_SCSI_STATUS ) + { + scsi_data.regs[WD_AUXILIARY_STATUS] &= ~ASR_INT; + + if (intf && intf->irq_callback) + { + intf->irq_callback(space->machine(), 0); + } + + LOG(( "WD33C93: PC=%08x - Status read (%02x)\n", cpu_get_pc(&space->device()), scsi_data.regs[WD_SCSI_STATUS] )); + } + else if ( scsi_data.sasr == WD_DATA ) + { + /* we're going to be doing synchronous reads */ + + /* get the transfer count */ + int count = wd33c93_get_xfer_count(); + + /* initialize the return value */ + scsi_data.regs[WD_DATA] = 0; + + if ( count <= 0 && scsi_data.busphase == PHS_MESS_IN ) + { + /* move to disconnect */ + wd33c93_complete_cmd(CSR_DISC); + } + else if ( count == 1 && scsi_data.busphase == PHS_STATUS ) + { + /* update the count */ + wd33c93_set_xfer_count( 0 ); + + /* move to message in phase */ + scsi_data.busphase = PHS_MESS_IN; + + /* complete the command */ + wd33c93_complete_cmd(CSR_XFER_DONE | scsi_data.busphase); + } + else if ( count-- > 0 ) /* make sure we still have data to send */ + { + if ( scsi_data.read_pending ) + { + int len = TEMP_INPUT_LEN; + + if ( (count+1) < len ) len = count+1; + wd33c93_read_data( len, &scsi_data.temp_input[0] ); + scsi_data.temp_input_pos = 0; + scsi_data.read_pending = 0; + } + + scsi_data.regs[WD_AUXILIARY_STATUS] &= ~ASR_INT; + + /* read in one byte */ + if ( scsi_data.temp_input_pos < TEMP_INPUT_LEN ) + scsi_data.regs[WD_DATA] = scsi_data.temp_input[scsi_data.temp_input_pos++]; + + /* update the count */ + wd33c93_set_xfer_count( count ); + + /* transfer finished, see where we're at */ + if ( count == 0 ) + { + if ( scsi_data.regs[WD_COMMAND_PHASE] != 0x60 ) + { + /* move to status phase */ + scsi_data.busphase = PHS_STATUS; + + /* complete the command */ + wd33c93_complete_cmd(CSR_XFER_DONE | scsi_data.busphase); + } + else + { + scsi_data.regs[WD_AUXILIARY_STATUS] |= ASR_INT; + scsi_data.regs[WD_AUXILIARY_STATUS] &= ~ASR_DBR; + } + } + } + } + + LOG(( "WD33C93: PC=%08x - Data read (%02x)\n", cpu_get_pc(&space->device()), scsi_data.regs[WD_DATA] )); + + /* get the register value */ + ret = scsi_data.regs[scsi_data.sasr]; + + /* auto-increment register select if not on special registers */ + if ( scsi_data.sasr != WD_COMMAND && scsi_data.sasr != WD_DATA && scsi_data.sasr != WD_AUXILIARY_STATUS ) + { + scsi_data.sasr = ( scsi_data.sasr + 1 ) & 0x1f; + } + + return ret; + } + + default: + { + logerror( "WD33C93: Read from invalid offset %d\n", offset ); + } + break; + } + + return 0; +} + +void wd33c93_init( running_machine &machine, const struct WD33C93interface *interface ) +{ + int i; + + // save interface pointer for later + intf = interface; + + memset(&scsi_data, 0, sizeof(scsi_data)); + memset(devices, 0, sizeof(devices)); + + // try to open the devices + for (i = 0; i < interface->scsidevs->devs_present; i++) + { + SCSIAllocInstance( machine, interface->scsidevs->devices[i].scsiClass, &devices[interface->scsidevs->devices[i].scsiID], interface->scsidevs->devices[i].diskregion ); + } + + /* allocate a timer for commands */ + scsi_data.cmd_timer = machine.scheduler().timer_alloc(FUNC(wd33c93_complete_cb)); + + scsi_data.temp_input = auto_alloc_array( machine, UINT8, TEMP_INPUT_LEN ); + +// state_save_register_item_array(machine, "wd33c93", NULL, 0, scsi_data); +} + +void wd33c93_exit( const struct WD33C93interface *interface ) +{ + int i; + + for (i = 0; i < interface->scsidevs->devs_present; i++) + { + SCSIDeleteInstance( devices[interface->scsidevs->devices[i].scsiID] ); + } +} + +void wd33c93_get_dma_data( int bytes, UINT8 *pData ) +{ + int len = bytes; + + if ( len >= wd33c93_get_xfer_count() ) + len = wd33c93_get_xfer_count(); + + if ( len == 0 ) + return; + + if ( (scsi_data.temp_input_pos+len) >= TEMP_INPUT_LEN ) + { + logerror( "Reading past end of buffer, increase TEMP_INPUT_LEN size\n" ); + len = TEMP_INPUT_LEN - len; + } + + assert(len); + + memcpy( pData, &scsi_data.temp_input[scsi_data.temp_input_pos], len ); + + scsi_data.temp_input_pos += len; + len = wd33c93_get_xfer_count() - len; + wd33c93_set_xfer_count(len); +} + +void wd33c93_write_data(int bytes, UINT8 *pData) +{ + UINT8 unit = wd33c93_getunit(); + + if (devices[unit]) + { + SCSIWriteData( devices[unit], pData, bytes ); + } + else + { + logerror("wd33c93: request for unknown device SCSI ID %d\n", unit); + } +} + +void *wd33c93_get_device(int id) +{ + void *ret; + + if (devices[id]) + { + SCSIGetDevice( devices[id], &ret ); + return ret; + } + + return NULL; +} + +void wd33c93_clear_dma(void) +{ + /* indicate DMA completed by clearing the transfer count */ + wd33c93_set_xfer_count(0); + scsi_data.regs[WD_AUXILIARY_STATUS] &= ~ASR_DBR; +} + +int wd33c93_get_dma_count(void) +{ + return wd33c93_get_xfer_count(); +} |