diff options
author | 2019-03-25 23:13:40 +0100 | |
---|---|---|
committer | 2019-03-25 23:13:40 +0100 | |
commit | b380514764cf857469bae61c11143a19f79a74c5 (patch) | |
tree | 63c8012e262618f08a332da31dd714281aa2c5ed /src/devices/machine/wd33c93.cpp | |
parent | c24473ddff715ecec2e258a6eb38960cf8c8e98e (diff) |
Revert "conflict resolution (nw)"
This reverts commit c24473ddff715ecec2e258a6eb38960cf8c8e98e, reversing
changes made to 009cba4fb8102102168ef32870892438327f3705.
Diffstat (limited to 'src/devices/machine/wd33c93.cpp')
-rw-r--r-- | src/devices/machine/wd33c93.cpp | 984 |
1 files changed, 984 insertions, 0 deletions
diff --git a/src/devices/machine/wd33c93.cpp b/src/devices/machine/wd33c93.cpp new file mode 100644 index 00000000000..fcbb45957af --- /dev/null +++ b/src/devices/machine/wd33c93.cpp @@ -0,0 +1,984 @@ +// license:BSD-3-Clause +// copyright-holders:ElSemi, R. Belmont, Ryan Holtz +/* + * 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 LOG_READS (1 << 0) +#define LOG_WRITES (1 << 1) +#define LOG_COMMANDS (1 << 2) +#define LOG_ERRORS (1 << 3) +#define LOG_MISC (1 << 4) +#define LOG_REGS (LOG_READS | LOG_WRITES) +#define LOG_ALL (LOG_REGS | LOG_COMMANDS | LOG_ERRORS | LOG_MISC) + +#define VERBOSE (0) +#include "logmacro.h" + + +/* 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 + +/* convernience functions */ +uint8_t wd33c93_device::getunit() +{ + /* return the destination unit id */ + return m_regs[WD_DESTINATION_ID] & SRCID_MASK; +} + +void wd33c93_device::set_xfer_count( int count ) +{ + /* set the count */ + m_regs[WD_TRANSFER_COUNT_LSB] = count & 0xff; + m_regs[WD_TRANSFER_COUNT] = (count >> 8) & 0xff; + m_regs[WD_TRANSFER_COUNT_MSB] = (count >> 16) & 0xff; +} + +int wd33c93_device::get_xfer_count() +{ + /* get the count */ + int count = m_regs[WD_TRANSFER_COUNT_MSB]; + + count <<= 8; + count |= m_regs[WD_TRANSFER_COUNT]; + count <<= 8; + count |= m_regs[WD_TRANSFER_COUNT_LSB]; + + return count; +} + +void wd33c93_device::complete_immediate(int status) +{ + /* reset our timer */ + m_cmd_timer->reset(); + + /* set the new status */ + m_regs[WD_SCSI_STATUS] = status & 0xff; + + /* set interrupt pending */ + m_regs[WD_AUXILIARY_STATUS] |= ASR_INT; + + /* check for error conditions */ + if (get_xfer_count() > 0) + { + /* set data buffer ready */ + m_regs[WD_AUXILIARY_STATUS] |= ASR_DBR; + } + else + { + /* clear data buffer ready */ + m_regs[WD_AUXILIARY_STATUS] &= ~ASR_DBR; + } + + /* clear command in progress and bus busy */ + m_regs[WD_AUXILIARY_STATUS] &= ~(ASR_CIP | ASR_BSY); + + /* if we have a callback, call it */ + if (!m_irq_cb.isnull()) + { + m_irq_cb(1); + } +} + +void wd33c93_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr) +{ + switch (tid) + { + case 0: + complete_immediate(param); + break; + + case 1: + complete_immediate(CSR_SRV_REQ | m_busphase); + break; + + case 2: + m_regs[WD_AUXILIARY_STATUS] &= ~ASR_CIP; + break; + } +} + +void wd33c93_device::complete_cmd(uint8_t status) +{ + /* fire off a timer to complete the command */ + m_cmd_timer->adjust(attotime::from_usec(1), status); +} + +/* command handlers */ +void wd33c93_device::unimplemented_cmd() +{ + LOGMASKED(LOG_COMMANDS | LOG_ERRORS, "%s: Unimplemented SCSI controller command: %02x\n", machine().describe_context(), m_regs[WD_COMMAND]); + + /* complete the command */ + complete_cmd(CSR_INVALID); +} + +void wd33c93_device::invalid_cmd() +{ + LOGMASKED(LOG_COMMANDS | LOG_ERRORS, "%s: Invalid SCSI controller command: %02x\n", machine().describe_context(), m_regs[WD_COMMAND]); + + /* complete the command */ + complete_cmd(CSR_INVALID); +} + +void wd33c93_device::reset_cmd() +{ + int advanced = 0; + + /* see if it wants us to reset with advanced features */ + if (m_regs[WD_OWN_ID] & OWNID_EAF) + { + advanced = 1; + } + + /* clear out all registers */ + memset(m_regs, 0, sizeof(m_regs)); + + /* complete the command */ + complete_cmd(advanced ? CSR_RESET_AF : CSR_RESET); +} + +void wd33c93_device::abort_cmd() +{ + /* complete the command */ + complete_cmd(CSR_ABORT); +} + +void wd33c93_device::disconnect_cmd() +{ + /* complete the command */ + m_regs[WD_AUXILIARY_STATUS] &= ~(ASR_CIP | ASR_BSY); +} + +void wd33c93_device::select_cmd() +{ + uint8_t unit = getunit(); + uint8_t newstatus; + + /* see if we can select that device */ + if (select(unit)) + { + /* device is available - signal selection done */ + newstatus = CSR_SELECT; + + /* determine the next bus phase depending on the command */ + if ((m_regs[WD_COMMAND] & 0x7f) == WD_CMD_SEL_ATN) + { + /* /ATN asserted during select: Move to Message Out Phase to read identify */ + m_busphase = PHS_MESS_OUT; + } + else + { + /* No /ATN asserted: Move to Command Phase */ + m_busphase = PHS_COMMAND; + } + + /* queue up a service request out in the future */ + m_service_req_timer->adjust( attotime::from_usec(50) ); + } + else + { + /* device is not available */ + newstatus = CSR_TIMEOUT; + } + + /* complete the command */ + complete_cmd(newstatus); +} + +void wd33c93_device::selectxfer_cmd() +{ + uint8_t unit = getunit(); + uint8_t newstatus; + + /* see if we can select that device */ + if (select(unit)) + { + if (m_regs[WD_COMMAND_PHASE] < 0x45) + { + /* device is available */ + + /* do the request */ + send_command(&m_regs[WD_CDB_1], 12); + int phase = get_phase(); + + /* set transfer count */ + if (get_xfer_count() > TEMP_INPUT_LEN) + { + LOGMASKED(LOG_ERRORS, "WD33C93: Transfer count too big. Please increase TEMP_INPUT_LEN (size=%d)\n", get_xfer_count()); + set_xfer_count(TEMP_INPUT_LEN); + } + + switch (phase) + { + case SCSI_PHASE_DATAIN: + m_read_pending = true; + break; + } + } + + if (m_read_pending) + { + int len = TEMP_INPUT_LEN; + + if (get_xfer_count() < len) + len = get_xfer_count(); + + memset(&m_temp_input[0], 0, TEMP_INPUT_LEN); + read_data(&m_temp_input[0], len); + m_temp_input_pos = 0; + m_read_pending = false; + } + + m_regs[WD_TARGET_LUN] = 0; + m_regs[WD_CONTROL] |= CTRL_EDI; + m_regs[WD_COMMAND_PHASE] = 0x60; + + /* signal transfer ready */ + newstatus = CSR_SEL_XFER_DONE; + + /* if allowed disconnect, queue a service request */ + if (m_identify & 0x40) + { + /* queue disconnect message in */ + m_busphase = PHS_MESS_IN; + + /* queue up a service request out in the future */ + m_service_req_timer->adjust(attotime::from_usec(50)); + } + } + else + { + /* device is not available */ + newstatus = CSR_TIMEOUT; + + set_xfer_count(0); + } + + /* complete the command */ + complete_cmd(newstatus); +} + +void wd33c93_device::negate_ack() +{ + LOGMASKED(LOG_MISC, "WD33C93: ACK Negated\n"); + + /* complete the command */ + m_regs[WD_AUXILIARY_STATUS] &= ~(ASR_CIP | ASR_BSY); +} + +void wd33c93_device::xferinfo_cmd() +{ + /* make the buffer available right away */ + m_regs[WD_AUXILIARY_STATUS] |= ASR_DBR; + m_regs[WD_AUXILIARY_STATUS] |= ASR_CIP; + + /* the command will be completed once the data is transferred */ + m_deassert_cip_timer->adjust(attotime::from_msec(1)); +} + +/* Handle pending commands */ +void wd33c93_device::dispatch_command() +{ + /* get the command */ + uint8_t cmd = m_regs[WD_COMMAND] & 0x7f; + + switch (cmd) + { + case WD_CMD_RESET: + LOGMASKED(LOG_COMMANDS, "WD33C93: %s - Reset Command\n", machine().describe_context()); + reset_cmd(); + break; + + case WD_CMD_ABORT: + LOGMASKED(LOG_COMMANDS, "WD33C93: %s - Abort Command\n", machine().describe_context()); + abort_cmd(); + break; + + case WD_CMD_NEGATE_ACK: + LOGMASKED(LOG_COMMANDS, "WD33C93: %s - Negate ACK Command\n", machine().describe_context()); + negate_ack(); + break; + + case WD_CMD_DISCONNECT: + LOGMASKED(LOG_COMMANDS, "WD33C93: %s - Disconnect Command\n", machine().describe_context()); + disconnect_cmd(); + break; + + case WD_CMD_SEL_ATN: + case WD_CMD_SEL: + LOGMASKED(LOG_COMMANDS, "WD33C93: %s - Select %sCommand\n", machine().describe_context(), cmd == WD_CMD_SEL_ATN ? "w/ ATN " : ""); + select_cmd(); + break; + + case WD_CMD_SEL_ATN_XFER: + case WD_CMD_SEL_XFER: + LOGMASKED(LOG_COMMANDS, "WD33C93: %s - Select %sand Xfer Command\n", machine().describe_context(), cmd == WD_CMD_SEL_ATN ? "w/ ATN " : ""); + selectxfer_cmd(); + break; + + case WD_CMD_TRANS_INFO: + LOGMASKED(LOG_COMMANDS, "WD33C93: %s - Transfer Info Command\n", machine().describe_context()); + xferinfo_cmd(); + break; + + case WD_CMD_ASSERT_ATN: + case WD_CMD_RESELECT: + case WD_CMD_RESEL_RECEIVE: + case WD_CMD_RESEL_SEND: + case WD_CMD_WAIT_SEL_RECEIVE: + case WD_CMD_SSCC: + case WD_CMD_SND_DISC: + case WD_CMD_SET_IDI: + case WD_CMD_RCV_CMD: + case WD_CMD_RCV_DATA: + case WD_CMD_RCV_MSG_OUT: + case WD_CMD_RCV: + case WD_CMD_SND_STATUS: + case WD_CMD_SND_DATA: + case WD_CMD_SND_MSG_IN: + case WD_CMD_SND: + case WD_CMD_TRANS_ADDR: + case WD_CMD_XFER_PAD: + case WD_CMD_TRANSFER_PAD: + unimplemented_cmd(); + break; + + default: + invalid_cmd(); + break; + } +} + +WRITE8_MEMBER(wd33c93_device::write) +{ + switch (offset) + { + case 0: + { + /* update register select */ + m_sasr = data & 0x1f; + } + break; + + case 1: + { + /* update the register */ + if (m_sasr != WD_SCSI_STATUS && m_sasr <= WD_QUEUE_TAG) + { + m_regs[m_sasr] = data; + } + + switch (m_sasr) + { + case WD_OWN_ID: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Own ID Register (CDB Size) = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_CONTROL: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Control Register = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_TIMEOUT_PERIOD: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Timeout Period Register = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_CDB_1: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Total Sectors Register (CDB1) = %02x\n", machine().describe_context(), m_sasr, data); + m_regs[WD_COMMAND_PHASE] = 0; + break; + case WD_CDB_2: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Total Heads Register (CDB2) = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_CDB_3: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Total Cylinders Register MSB (CDB3) = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_CDB_4: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Total Cylinders Register LSB (CDB4) = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_CDB_5: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Logical Address Register MSB (CDB5) = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_CDB_6: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Logical Address Register 2nd (CDB6) = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_CDB_7: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Logical Address Register 3rd (CDB7) = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_CDB_8: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Logical Address Register LSB (CDB8) = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_CDB_9: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Sector Number Register (CDB9) = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_CDB_10: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Head Number Register (CDB10) = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_CDB_11: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Cylinder Number Register MSB (CDB11) = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_CDB_12: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Cylinder Number Register LSB (CDB12) = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_TARGET_LUN: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Target LUN Register = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_COMMAND_PHASE: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Command Phase Register = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_SYNCHRONOUS_TRANSFER: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Synchronous Transfer Register = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_TRANSFER_COUNT_MSB: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Transfer Count Register MSB = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_TRANSFER_COUNT: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Transfer Count Register 2nd = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_TRANSFER_COUNT_LSB: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Transfer Count Register LSB = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_DESTINATION_ID: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Destination ID Register = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_SOURCE_ID: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Source ID Register = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_SCSI_STATUS: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, SCSI Status Register (read-only!) = %02x (ignored)\n", machine().describe_context(), m_sasr, data); + break; + case WD_COMMAND: + /* if we receive a command, schedule to process it */ + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Command Register = %02x - unit %d\n", machine().describe_context(), m_sasr, data, getunit()); + + /* signal we're processing it */ + m_regs[WD_AUXILIARY_STATUS] |= ASR_CIP; + + /* process the command */ + dispatch_command(); + break; + case WD_DATA: + { + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Data Register = %02x\n", machine().describe_context(), m_sasr, data); + + /* if data was written, and we have a count, send to device */ + int count = get_xfer_count(); + + if (m_regs[WD_COMMAND] & 0x80) + count = 1; + + if (count-- > 0) + { + /* write to FIFO */ + if (m_fifo_pos < FIFO_SIZE) + { + m_fifo[m_fifo_pos++] = data; + } + + /* update count */ + set_xfer_count(count); + + /* if we're done with the write, see where we're at */ + if (count == 0) + { + m_regs[WD_AUXILIARY_STATUS] |= ASR_INT; + m_regs[WD_AUXILIARY_STATUS] &= ~ASR_DBR; + + switch (m_busphase) + { + case PHS_MESS_OUT: + { + /* reset fifo */ + m_fifo_pos = 0; + + /* Message out phase. Data is probably SCSI Identify. Move to command phase. */ + m_busphase = PHS_COMMAND; + + m_identify = m_fifo[0]; + } + break; + + case PHS_COMMAND: + { + /* Execute the command. Depending on the command, we'll move to data in or out */ + send_command(&m_fifo[0], 12); + int xfercount = get_length(); + int phase = get_phase(); + + /* reset fifo */ + m_fifo_pos = 0; + + /* set the new count */ + set_xfer_count(xfercount); + + switch (phase) + { + case SCSI_PHASE_STATUS: + m_busphase = PHS_STATUS; + break; + + case SCSI_PHASE_DATAIN: + m_busphase = PHS_DATA_IN; + m_read_pending = true; + break; + + case SCSI_PHASE_DATAOUT: + m_busphase = PHS_DATA_OUT; + break; + } + } + break; + + case PHS_DATA_OUT: + { + /* write data out to device */ + write_data(m_fifo, m_fifo_pos); + + /* reset fifo */ + m_fifo_pos = 0; + + /* move to status phase */ + m_busphase = PHS_STATUS; + } + break; + } + + /* complete the command */ + complete_immediate(CSR_XFER_DONE | m_busphase); + } + } + else + { + LOGMASKED(LOG_MISC | LOG_ERRORS, "WD33C93: Sending data to device with transfer count = 0!. Ignoring...\n"); + } + break; + } + case WD_QUEUE_TAG: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Queue Tag Register = %02x\n", machine().describe_context(), m_sasr, data); + break; + case WD_AUXILIARY_STATUS: + LOGMASKED(LOG_WRITES, "WD33C93: %s - Write Register %02x, Auxiliary Status Register (read-only!) = %02x (ignored)\n", machine().describe_context(), m_sasr, data); + break; + default: + LOGMASKED(LOG_WRITES | LOG_ERRORS, "WD33C93: %s - Write Register %02x, Unknown = %02x (ignored)\n", machine().describe_context(), m_sasr, data); + break; + } + + /* auto-increment register select if not on special registers */ + if (m_sasr != WD_COMMAND && m_sasr != WD_DATA && m_sasr != WD_AUXILIARY_STATUS) + { + m_sasr = (m_sasr + 1) & 0x1f; + } + } + break; + + default: + { + LOGMASKED(LOG_ERRORS, "WD33C93: Write to invalid offset %d (data=%02x)\n", offset, data); + } + break; + } +} + +READ8_MEMBER(wd33c93_device::read) +{ + switch (offset) + { + case 0: + /* read aux status */ + return m_regs[WD_AUXILIARY_STATUS]; + + case 1: + { + switch (m_sasr) + { + case WD_OWN_ID: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Own ID Register (CDB Size) (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_CONTROL: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Control Register (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_TIMEOUT_PERIOD: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Timeout Period Register (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_CDB_1: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Total Sectors Register (CDB1) (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + m_regs[WD_COMMAND_PHASE] = 0; + break; + case WD_CDB_2: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Total Heads Register (CDB2) (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_CDB_3: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Total Cylinders Register MSB (CDB3) (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_CDB_4: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Total Cylinders Register LSB (CDB4) (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_CDB_5: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Logical Address Register MSB (CDB5) (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_CDB_6: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Logical Address Register 2nd (CDB6) (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_CDB_7: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Logical Address Register 3rd (CDB7) (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_CDB_8: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Logical Address Register LSB (CDB8) (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_CDB_9: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Sector Number Register (CDB9) (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_CDB_10: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Head Number Register (CDB10) (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_CDB_11: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Cylinder Number Register MSB (CDB11) (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_CDB_12: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Cylinder Number Register LSB (CDB12) (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_TARGET_LUN: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Target LUN Register (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_COMMAND_PHASE: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Command Phase Register (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_SYNCHRONOUS_TRANSFER: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Synchronous Transfer Register (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_TRANSFER_COUNT_MSB: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Transfer Count Register MSB (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_TRANSFER_COUNT: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Transfer Count Register 2nd (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_TRANSFER_COUNT_LSB: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Transfer Count Register LSB (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_DESTINATION_ID: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Destination ID Register (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_SOURCE_ID: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Source ID Register (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_SCSI_STATUS: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, SCSI Status Register (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + m_regs[WD_AUXILIARY_STATUS] &= ~ASR_INT; + + /* if reading status, clear irq flag */ + if (!m_irq_cb.isnull()) + { + m_irq_cb(0); + } + break; + case WD_COMMAND: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Command Register (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_DATA: + { + /* we're going to be doing synchronous reads */ + + /* get the transfer count */ + int count = get_xfer_count(); + + /* initialize the return value */ + m_regs[WD_DATA] = 0; + + if (count <= 0 && m_busphase == PHS_MESS_IN) + { + /* move to disconnect */ + complete_cmd(CSR_DISC); + } + else if (count == 1 && m_busphase == PHS_STATUS) + { + /* update the count */ + set_xfer_count(0); + + /* move to message in phase */ + m_busphase = PHS_MESS_IN; + + /* complete the command */ + complete_cmd(CSR_XFER_DONE | m_busphase); + } + else if (count-- > 0) /* make sure we still have data to send */ + { + if (m_read_pending) + { + int len = TEMP_INPUT_LEN; + + if ((count + 1) < len ) + len = count + 1; + read_data(&m_temp_input[0], len); + m_temp_input_pos = 0; + m_read_pending = false; + } + + m_regs[WD_AUXILIARY_STATUS] &= ~ASR_INT; + + /* read in one byte */ + if (m_temp_input_pos < TEMP_INPUT_LEN) + m_regs[WD_DATA] = m_temp_input[m_temp_input_pos++]; + + /* update the count */ + set_xfer_count(count); + + /* transfer finished, see where we're at */ + if (count == 0) + { + if (m_regs[WD_COMMAND_PHASE] != 0x60) + { + /* move to status phase */ + m_busphase = PHS_STATUS; + + /* complete the command */ + complete_cmd(CSR_XFER_DONE | m_busphase); + } + else + { + m_regs[WD_AUXILIARY_STATUS] |= ASR_INT; + m_regs[WD_AUXILIARY_STATUS] &= ~ASR_DBR; + } + } + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Data Register (%02x)\n", machine().describe_context(), WD_DATA, m_regs[WD_DATA]); + } + break; + } + case WD_QUEUE_TAG: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Queue Tag Register (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + case WD_AUXILIARY_STATUS: + LOGMASKED(LOG_READS, "WD33C93: %s - Read Register %02x, Auxiliary Status Register (%02x)\n", machine().describe_context(), m_sasr, m_regs[m_sasr]); + break; + default: + LOGMASKED(LOG_READS | LOG_ERRORS, "WD33C93: %s - Read Register %02x, Unknown\n", machine().describe_context(), m_sasr); + break; + } + + /* get the register value */ + uint8_t ret = 0xff; + if (m_sasr == WD_AUXILIARY_STATUS || m_sasr <= WD_QUEUE_TAG) + ret = m_regs[m_sasr]; + + /* auto-increment register select if not on special registers */ + if (m_sasr != WD_COMMAND && m_sasr != WD_DATA && m_sasr != WD_AUXILIARY_STATUS) + { + m_sasr = (m_sasr + 1) & 0x1f; + } + + return ret; + } + + default: + LOGMASKED(LOG_READS | LOG_ERRORS, "WD33C93: Read from invalid offset %d\n", offset); + break; + } + + return 0; +} + +wd33c93_device::wd33c93_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + legacy_scsi_host_adapter(mconfig, WD33C93, tag, owner, clock), + m_irq_cb(*this) +{ +} + +void wd33c93_device::device_start() +{ + legacy_scsi_host_adapter::device_start(); + + memset(m_regs, 0, sizeof(m_regs)); + memset(m_fifo, 0, sizeof(m_fifo)); + memset(m_temp_input, 0, sizeof(m_temp_input)); + + m_sasr = 0; + m_fifo_pos = 0; + m_temp_input_pos = 0; + m_busphase = 0; + m_identify = 0; + m_read_pending = 0; + + m_irq_cb.resolve(); + + /* allocate a timer for commands */ + m_cmd_timer = timer_alloc(0); + m_service_req_timer = timer_alloc(1); + m_deassert_cip_timer = timer_alloc(2); + + save_item(NAME(m_sasr)); + save_item(NAME(m_regs)); + save_item(NAME(m_fifo)); + save_item(NAME(m_fifo_pos)); + save_item(NAME(m_temp_input)); + save_item(NAME(m_temp_input_pos)); + save_item(NAME(m_busphase)); + save_item(NAME(m_identify)); + save_item(NAME(m_read_pending)); +} + +int wd33c93_device::dma_read_data(int bytes, uint8_t *data) +{ + int len = bytes; + + if (len >= get_xfer_count()) + len = get_xfer_count(); + + if (len == 0) + return 0; + + if ((m_temp_input_pos + len) >= TEMP_INPUT_LEN) + { + LOGMASKED(LOG_ERRORS, "Reading past end of buffer, increase TEMP_INPUT_LEN size\n"); + len = TEMP_INPUT_LEN - len; + } + + assert(len); + + memcpy(data, &m_temp_input[m_temp_input_pos], len); + + m_temp_input_pos += len; + set_xfer_count(get_xfer_count() - len); + return len; +} + +void wd33c93_device::dma_write_data(int bytes, uint8_t *data) +{ + write_data(data, bytes); +} + +void wd33c93_device::clear_dma() +{ + /* indicate DMA completed by clearing the transfer count */ + set_xfer_count(0); + m_regs[WD_AUXILIARY_STATUS] &= ~ASR_DBR; +} + +int wd33c93_device::get_dma_count() +{ + return get_xfer_count(); +} + +DEFINE_DEVICE_TYPE(WD33C93, wd33c93_device, "wd33c93", "Western Digital WD33C93 SCSI") |