diff --git a/.gitattributes b/.gitattributes index 686a91351f9..6745e070230 100644 --- a/.gitattributes +++ b/.gitattributes @@ -909,6 +909,8 @@ src/emu/machine/msm58321.c svneol=native#text/plain src/emu/machine/msm58321.h svneol=native#text/plain src/emu/machine/msm6242.c svneol=native#text/plain src/emu/machine/msm6242.h svneol=native#text/plain +src/emu/machine/ncr539x.c svneol=native#text/plain +src/emu/machine/ncr539x.h svneol=native#text/plain src/emu/machine/nmc9306.c svneol=native#text/plain src/emu/machine/nmc9306.h svneol=native#text/plain src/emu/machine/nvram.c svneol=native#text/plain diff --git a/src/emu/emu.mak b/src/emu/emu.mak index 692d3ef7bb4..78bf9fce321 100644 --- a/src/emu/emu.mak +++ b/src/emu/emu.mak @@ -215,6 +215,7 @@ EMUMACHINEOBJS = \ $(EMUMACHINE)/msm5832.o \ $(EMUMACHINE)/msm58321.o \ $(EMUMACHINE)/msm6242.o \ + $(EMUMACHINE)/ncr539x.o \ $(EMUMACHINE)/nmc9306.o \ $(EMUMACHINE)/nvram.o \ $(EMUMACHINE)/pc16552d.o \ diff --git a/src/emu/machine/ncr539x.c b/src/emu/machine/ncr539x.c new file mode 100644 index 00000000000..fc16d62cd43 --- /dev/null +++ b/src/emu/machine/ncr539x.c @@ -0,0 +1,912 @@ +/* + * ncr539x.c + * + * NCR 53(CF)94/53(CF)96 SCSI controller + * Includes enhanced features of the AMD 53CF94/96 and compatibles + * + * All new emulation in 2011 by R. Belmont. + * + */ + +#include "emu.h" +#include "ncr539x.h" + +#define VERBOSE (0) +#define VERBOSE_READS (0) + +enum +{ + TIMER_539X_COMMAND, + + TIMER_539X_END +}; + +#define MAIN_STATUS_INTERRUPT 0x80 +#define MAIN_STATUS_ILLEGAL_OPER 0x40 +#define MAIN_STATUS_PARITY_ERROR 0x20 +#define MAIN_STATUS_COUNT_TO_ZERO 0x10 +#define MAIN_STATUS_GROUP_VALID 0x08 +#define MAIN_STATUS_MESSAGE 0x04 +#define MAIN_STATUS_CMD_DATA 0x02 +#define MAIN_STATUS_IO 0x01 + +#define IRQ_STATUS_RESET 0x80 +#define IRQ_STATUS_INVALID_COMMAND 0x40 +#define IRQ_STATUS_DISCONNECTED 0x20 +#define IRQ_STATUS_SERVICE_REQUEST 0x10 +#define IRQ_STATUS_SUCCESS 0x08 +#define IRQ_STATUS_RESELECTED 0x04 // we were reselected as a target +#define IRQ_STATUS_SELECTED_WITH_ATN 0x02 // we were selected as a target with ATN steps +#define IRQ_STATUS_SELECTED 0x01 // we were selected as a target + +#define CR2_ALIGN_ENABLE 0x80 +#define CR2_FEATURES_ENABLE 0x40 +#define CR2_BYTE_ORDER 0x20 +#define CR2_TRISTATE_DMA 0x10 +#define CR2_SCSI2_ENABLE 0x08 +#define CR2_ABORT_ON_PARITY_ERROR 0x04 +#define CR2_GENERATE_REGISTER_PARITY 0x02 +#define CR2_GENERATE_DATA_PARITY 0x01 + +#if VERBOSE +#if VERBOSE_READS +static const char *rdregs[16] = { + "Transfer count LSB", // 0 + "Transfer count MSB", // 1 + "FIFO", // 2 + "Command", // 3 + "Status", // 4 + "Interrupt Status", // 5 + "Internal State", + "Current FIFO/Internal State", + "Control Register 1", + "0x9", + "0xA", + "Control Register 2", + "Control Register 3", + "Control Register 4", + "Transfer count HSB/Chip ID", + "0xF" +}; +#endif + +static const char *wrregs[16] = { + "Start Transfer count LSB", + "Start Transfer count MSB", + "FIFO", + "Command", + "SCSI Destination ID", + "SCSI Timeout", + "Synchronous Transfer Period", + "Synchronous Offset", + "Control Register 1", + "Clock Factor", + "Forced Test Mode", + "Control Register 2", + "Control Register 3", + "Control Register 4", + "Start Transfer count HSB", + "Data Alignment" +}; +#endif + +// get the length of a SCSI command based on its command byte type +static int get_cmd_len(int cbyte) +{ + int group; + + group = (cbyte>>5) & 7; + + if (group == 0) return 6; + if (group == 1 || group == 2) return 10; + if (group == 5) return 12; + + return 6; +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void ncr539x_device::device_config_complete() +{ + // inherit a copy of the static data + const NCR539Xinterface *intf = reinterpret_cast(static_config()); + if (intf != NULL) + { + *static_cast(this) = *intf; + } + + // or initialize to defaults if none provided + else + { + scsidevs = NULL; + memset(&m_out_irq_cb, 0, sizeof(m_out_irq_cb)); + memset(&m_out_drq_cb, 0, sizeof(m_out_drq_cb)); + } +} + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +const device_type NCR539X = &device_creator; + +//------------------------------------------------- +// ncr539x_device - constructor/destructor +//------------------------------------------------- + +ncr539x_device::ncr539x_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, NCR539X, "539x SCSI", tag, owner, clock) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void ncr539x_device::device_start() +{ + int i; + + memset(m_scsi_devices, 0, sizeof(m_scsi_devices)); + + // resolve line callbacks + m_out_irq_func.resolve(m_out_irq_cb, *this); + m_out_drq_func.resolve(m_out_drq_cb, *this); + + // try to open the devices + for (i = 0; i < scsidevs->devs_present; i++) + { + SCSIAllocInstance( machine(), + scsidevs->devices[i].scsiClass, + &m_scsi_devices[scsidevs->devices[i].scsiID], + scsidevs->devices[i].diskregion ); + } + + m_operation_timer = timer_alloc(0, NULL); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void ncr539x_device::device_reset() +{ + memset(m_scsi_devices, 0, sizeof(m_scsi_devices)); + + m_fifo_ptr = 0; + m_irq_status = 0; + m_status = SCSI_PHASE_STATUS; + m_internal_state = 0; + m_buffer_offset = 512; + m_buffer_remaining = 0; + m_dma_size = 0; + m_xfer_count = 0; + m_total_data = 0; + m_selected = false; + m_control1 = m_control2 = m_control3 = m_control4 = 0; + m_chipid_available = false; + m_chipid_lock = false; + + m_out_irq_func(CLEAR_LINE); + m_out_drq_func(CLEAR_LINE); + + scan_devices(); +} + +//------------------------------------------------- +// device_stop - device-specific stop/shutdown +//------------------------------------------------- +void ncr539x_device::device_stop() +{ + int i; + + // clean up the devices + for (i = 0; i < scsidevs->devs_present; i++) + { + SCSIDeleteInstance( m_scsi_devices[scsidevs->devices[i].scsiID] ); + } +} + +void ncr539x_device::dma_read_data(int bytes, UINT8 *pData) +{ + if (m_scsi_devices[m_last_id]) + { + if (VERBOSE) + logerror("NCR539x: issuing read for %d bytes\n", bytes); + SCSIReadData(m_scsi_devices[m_last_id], pData, bytes); + } + else + { + logerror("ncr539x: read unknown device SCSI ID %d\n", m_last_id); + } +} + + +void ncr539x_device::dma_write_data(int bytes, UINT8 *pData) +{ + if (bytes) + { + if (m_scsi_devices[m_last_id]) + { + SCSIWriteData(m_scsi_devices[m_last_id], pData, bytes); + } + else + { + logerror("ncr539x: write to unknown device SCSI ID %d\n", m_last_id); + } + } +} + +void ncr539x_device::scan_devices() +{ + int i; + + // try to open the devices + for (i = 0; i < scsidevs->devs_present; i++) + { + // if a device wasn't already allocated + if (!m_scsi_devices[scsidevs->devices[i].scsiID]) + { + SCSIAllocInstance( machine(), + scsidevs->devices[i].scsiClass, + &m_scsi_devices[scsidevs->devices[i].scsiID], + scsidevs->devices[i].diskregion ); + } + } +} + +void ncr539x_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr) +{ + //printf("539X: device_timer expired, param = %d, m_command = %02x\n", param, m_command); + + switch (param) + { + case TIMER_539X_COMMAND: + // if this is a DMA command, raise DRQ now + if (m_command & 0x80) + { + m_out_drq_func(ASSERT_LINE); + } + + switch (m_command & 0x7f) + { + case 0x41: // select without ATN steps + if (m_scsi_devices[m_last_id]) + { + m_irq_status |= IRQ_STATUS_SERVICE_REQUEST | IRQ_STATUS_SUCCESS; + // we should now be in the command phase + m_status &= ~7; // clear bus phases + m_status |= MAIN_STATUS_INTERRUPT | SCSI_PHASE_COMMAND; + m_fifo_ptr = 0; + m_selected = true; + + #if VERBOSE + printf("Selecting w/o ATN, irq_status = %02x, status = %02x!\n", m_irq_status, m_status); + #endif + + // if DMA is not enabled, there should already be a command loaded into the FIFO + if (!(m_command & 0x80)) + { + exec_fifo(); + } + update_fifo_internal_state(0); + } + else + { + #if VERBOSE + printf("Select failed, no device @ ID %d!\n", m_last_id); + #endif + m_status |= MAIN_STATUS_INTERRUPT; + m_irq_status |= IRQ_STATUS_DISCONNECTED; + } + m_out_irq_func(ASSERT_LINE); + break; + + case 0x42: // Select with ATN steps + if (m_scsi_devices[m_last_id]) + { + m_irq_status |= IRQ_STATUS_SERVICE_REQUEST | IRQ_STATUS_SUCCESS; + // we should now be in the command phase + m_status &= ~7; // clear bus phases + m_status |= MAIN_STATUS_INTERRUPT | SCSI_PHASE_COMMAND; + m_fifo_ptr = 0; + m_selected = true; + #if VERBOSE + printf("Selecting with ATN, irq_status = %02x, status = %02x!\n", m_irq_status, m_status); + #endif + + // if DMA is not enabled, there should already be a command loaded into the FIFO + if (!(m_command & 0x80)) + { + exec_fifo(); + } + update_fifo_internal_state(0); + } + else + { + #if VERBOSE + printf("Select failed, no device @ ID %d!\n", m_last_id); + #endif + m_status |= MAIN_STATUS_INTERRUPT; + m_irq_status |= IRQ_STATUS_DISCONNECTED; + } + m_out_irq_func(ASSERT_LINE); + break; + + case 0x11: // initiator command complete + #if VERBOSE + printf("Initiator command complete\n"); + #endif + m_irq_status = IRQ_STATUS_SERVICE_REQUEST; + m_status &= ~7; // clear phase bits + m_status |= MAIN_STATUS_INTERRUPT | SCSI_PHASE_DATAIN; // go to data in phase (?) + m_out_irq_func(ASSERT_LINE); + + // this puts status and message bytes into the FIFO (todo: what are these?) + m_fifo_ptr = 0; + m_xfer_count = 2; + m_buffer_remaining = m_total_data = 0; + m_fifo[0] = 0; // status byte + m_fifo[1] = 0; // message byte + m_selected = false; + update_fifo_internal_state(2); + break; + + case 0x12: // message accepted + #if VERBOSE + printf("Message accepted\n"); + #endif + m_irq_status = IRQ_STATUS_SERVICE_REQUEST; + m_status |= MAIN_STATUS_INTERRUPT; + m_out_irq_func(ASSERT_LINE); + break; + + default: + fatalerror("539x: Unhandled command %02x\n", m_command); + break; + } + break; + + default: + break; + } +} + +READ8_MEMBER( ncr539x_device::read ) +{ + UINT8 rv = 0; + + #if VERBOSE + #if VERBOSE_READS + printf("539x: Read @ %s (%02x) (PC=%x) (status %02x irq_status %02x)\n", rdregs[offset], offset, cpu_get_pc(&space.device()), m_status, m_irq_status); + #endif + #endif + + switch (offset) + { + case 0: + rv = m_xfer_count & 0xff; + break; + + case 1: + rv = (m_xfer_count>>8) & 0xff; + break; + + case 2: // FIFO + { + UINT8 fifo_bytes = m_fifo_internal_state & 0x1f; + + if (!fifo_bytes) + { + rv = 0; + } + else + { + rv = m_fifo[m_fifo_ptr++]; + + fifo_bytes--; + m_xfer_count--; + update_fifo_internal_state(fifo_bytes); + + #if VERBOSE + printf("Read %02x from FIFO[%d], FIFO now contains %d bytes (PC=%x, m_buffer_remaining %x)\n", rv, m_fifo_ptr-1, fifo_bytes, cpu_get_pc(&space.device()), m_buffer_remaining); + #endif + + if (fifo_bytes == 0) + { + // the last transfer command has more data for us + if (m_xfer_count > 0) + { + int fifo_fill_size = m_fifo_size; + if (m_xfer_count < fifo_fill_size) + { + fifo_fill_size = m_xfer_count; + } + memcpy(m_fifo, &m_buffer[m_buffer_offset], fifo_fill_size); + m_buffer_offset += fifo_fill_size; + m_buffer_remaining -= fifo_fill_size; + m_fifo_ptr = 0; + update_fifo_internal_state(fifo_fill_size); + #if VERBOSE + printf("Refreshing FIFO (%x remaining from transfer, %x in buffer, %x in total)\n", m_xfer_count, m_buffer_remaining, m_total_data); + #endif + } + else + { + #if VERBOSE + printf("FIFO empty, asserting service request (buffer_remaining %x)\n", m_buffer_remaining); + #endif + m_irq_status = IRQ_STATUS_SERVICE_REQUEST; + m_status &= 0x7; // clear everything but the phase bits + m_status |= MAIN_STATUS_INTERRUPT | MAIN_STATUS_COUNT_TO_ZERO; + m_out_irq_func(ASSERT_LINE); + + // if no data at all, drop the phase + if ((m_buffer_remaining + m_total_data) == 0) + { + #if VERBOSE + printf("Out of data, setting phase STATUS\n"); + #endif + m_status &= ~0x7; + m_status |= SCSI_PHASE_STATUS; + } + } + } + } + } + break; + + case 3: + rv = m_command; + break; + + case 4: + rv = m_status; + break; + + case 5: + rv = m_irq_status; + // clear the interrupt state + m_status &= ~MAIN_STATUS_INTERRUPT; + m_out_irq_func(CLEAR_LINE); + break; + + case 6: + rv = m_internal_state; + break; + + case 7: + rv = m_fifo_internal_state; + break; + + case 8: + rv = m_control1; + break; + + case 0xb: + rv = m_control2; + break; + + case 0xc: + rv = m_control3; + break; + + case 0xd: + rv = m_control4; + break; + + case 0xe: + if (m_control2 & CR2_FEATURES_ENABLE) + { + if (m_chipid_available) + { + rv = 0xa2; // 0x12 for CF94, 0xa2 for CF96 + } + else + { + rv = (m_xfer_count>>16) & 0xff; + } + } + break; + + } + return rv; +} + +WRITE8_MEMBER( ncr539x_device::write ) +{ + #if VERBOSE + if (offset != 2) printf("539x: Write %02x @ %s (%02x) (PC=%x)\n", data, wrregs[offset], offset, cpu_get_pc(&space.device())); + #endif + + switch (offset) + { + case 0: + m_dma_size &= 0xff00; + m_dma_size |= data; + break; + + case 1: + m_dma_size &= 0x00ff; + m_dma_size |= (data<<8); + break; + + case 2: // FIFO + fifo_write(data); + break; + + case 3: + m_command = data; + + // clear status bits (OK to do here?) + m_status &= ~MAIN_STATUS_INTERRUPT; + m_irq_status = 0; + + switch (data & 0x7f) + { + case 0x00: // NOP + m_irq_status = IRQ_STATUS_SUCCESS; + m_status |= MAIN_STATUS_INTERRUPT; + m_out_irq_func(ASSERT_LINE); + + // DMA NOP? allow chip ID + if ((m_command == 0x80) && (!m_chipid_lock)) + { + m_chipid_available = true; + } + break; + + case 0x01: // Clear FIFO (must not change buffer state) + m_fifo_ptr = 0; + update_fifo_internal_state(0); + m_irq_status = IRQ_STATUS_SUCCESS; + m_status |= MAIN_STATUS_INTERRUPT; + m_out_irq_func(ASSERT_LINE); + break; + + case 0x02: // Reset device + device_reset(); + + m_irq_status = IRQ_STATUS_SUCCESS; + m_status |= MAIN_STATUS_INTERRUPT; + m_out_irq_func(ASSERT_LINE); + break; + + case 0x03: // Reset SCSI bus + m_status = 0; + m_irq_status = IRQ_STATUS_SUCCESS; + m_status |= MAIN_STATUS_INTERRUPT; + m_out_irq_func(ASSERT_LINE); + break; + + case 0x10: // information transfer (must happen immediately) + m_status &= 0x7; // clear everything but the phase bits + m_status |= MAIN_STATUS_INTERRUPT; + m_irq_status = IRQ_STATUS_SUCCESS; + + int phase; + SCSIGetPhase( m_scsi_devices[m_last_id], &phase ); + + #if VERBOSE + printf("Information transfer: phase %d buffer remaining %x\n", phase, m_buffer_remaining); + #endif + + if (phase == SCSI_PHASE_DATAIN) // target -> initiator transfer + { + int amtToGet = m_buffer_size; + + // fill the internal sector buffer + if (m_buffer_remaining <= 0) + { + if (m_total_data < m_buffer_size) + { + amtToGet = m_total_data; + } + + #if VERBOSE + printf("amtToGet = %x\n", amtToGet); + #endif + + if (amtToGet > 0) + { + SCSIReadData(m_scsi_devices[m_last_id], m_buffer, amtToGet); + + m_total_data -= amtToGet; + m_buffer_offset = 0; + m_buffer_remaining = amtToGet; + } + } + + // copy the requested amount into the FIFO + if (amtToGet > 0) + { + if (m_buffer_remaining < m_dma_size) + { + m_dma_size = m_buffer_remaining; + } + + int fifo_fill_size = m_fifo_size; + + if (m_dma_size < fifo_fill_size) + { + fifo_fill_size = m_dma_size; + } + + #if VERBOSE + printf("filling FIFO from buffer[%x] for %x bytes\n", m_buffer_offset, fifo_fill_size); + #endif + + memcpy(m_fifo, &m_buffer[m_buffer_offset], fifo_fill_size); + m_buffer_offset += fifo_fill_size; + m_buffer_remaining -= fifo_fill_size; + + m_xfer_count = m_dma_size; + m_fifo_ptr = 0; + update_fifo_internal_state(fifo_fill_size); + m_out_drq_func(ASSERT_LINE); + } + + m_status |= MAIN_STATUS_COUNT_TO_ZERO; + + #if VERBOSE + printf("Information transfer: put %02x bytes into FIFO (dma size %x) (buffer remaining %x)\n", m_fifo_internal_state & 0x1f, m_dma_size, m_buffer_remaining); + #endif + } + else if (phase == SCSI_PHASE_DATAOUT) + { + m_xfer_count = m_dma_size; + if (m_xfer_count == 0) + { + m_xfer_count = 0x10000; + } + #if VERBOSE + printf("dma_size %x, xfer_count %x\n", m_dma_size, m_xfer_count); + #endif + m_status &= ~MAIN_STATUS_COUNT_TO_ZERO; + m_fifo_ptr = 0; + m_buffer_offset = 0; + m_buffer_remaining = 0; + } + m_out_irq_func(ASSERT_LINE); + break; + + case 0x24: // Terminate steps + #if VERBOSE + printf("Terminate steps\n"); + #endif + m_irq_status = IRQ_STATUS_SUCCESS | IRQ_STATUS_DISCONNECTED; + m_status |= MAIN_STATUS_INTERRUPT; + m_out_irq_func(ASSERT_LINE); + m_fifo_ptr = 0; + update_fifo_internal_state(0); + break; + + case 0x27: // Disconnect + #if VERBOSE + printf("Disconnect\n"); + #endif + m_irq_status = IRQ_STATUS_SUCCESS; + m_status |= MAIN_STATUS_INTERRUPT; + m_out_irq_func(ASSERT_LINE); + break; + + case 0x44: // Enable selection/reselection + #if VERBOSE + printf("Enable selection/reselection\n"); + #endif + m_irq_status = IRQ_STATUS_SUCCESS; + m_status |= MAIN_STATUS_INTERRUPT; + m_out_irq_func(ASSERT_LINE); + break; + + case 0x47: // Reselect with ATN3 steps + if (m_scsi_devices[m_last_id]) + { + m_irq_status |= IRQ_STATUS_SERVICE_REQUEST | IRQ_STATUS_SUCCESS; + // we should now be in the command phase + m_status &= ~7; // clear bus phases + m_status |= MAIN_STATUS_INTERRUPT | SCSI_PHASE_COMMAND; + m_fifo_ptr = 0; + m_selected = true; + #if VERBOSE + printf("Reselecting with ATN3, irq_status = %02x, status = %02x!\n", m_irq_status, m_status); + #endif + + // if DMA is not enabled, there should already be a command loaded into the FIFO + if (!(m_command & 0x80)) + { + exec_fifo(); + } + update_fifo_internal_state(0); + } + else + { + #if VERBOSE + printf("Reselect with ATN3 failed, no device @ ID %d!\n", m_last_id); + #endif + m_status |= MAIN_STATUS_INTERRUPT; + m_irq_status |= IRQ_STATUS_DISCONNECTED; + } + m_out_irq_func(ASSERT_LINE); + break; + + default: // other commands are not instantaneous + #if VERBOSE + printf("Setting timer for command %02x\n", data); + #endif + // 1x commands happen much faster + if ((m_command & 0x70) == 0x10) + { + m_operation_timer->adjust(attotime::from_hz(65536), TIMER_539X_COMMAND); + } + else + { + m_operation_timer->adjust(attotime::from_hz(16384), TIMER_539X_COMMAND); + } + break; + } + break; + + case 4: + m_last_id = data; + break; + + case 5: + m_timeout = data; + break; + + case 6: + m_sync_xfer_period = data; + break; + + case 7: + m_sync_offset = data; + break; + + case 8: + m_control1 = data; + break; + + case 9: + m_clock_factor = data; + break; + + case 0xa: + m_forced_test = data; + break; + + case 0xb: + m_control2 = data; + break; + + case 0xc: + m_control3 = data; + break; + + case 0xd: + m_control4 = data; + break; + + case 0xe: + if (m_control2 & CR2_FEATURES_ENABLE) + { + m_dma_size &= 0xffff; + m_dma_size |= (data<<16); + m_chipid_available = false; + m_chipid_lock = true; + } + break; + + case 0xf: + m_data_alignment = data; + break; + } +} + +void ncr539x_device::exec_fifo() +{ + int length, phase; + + SCSISetCommand(m_scsi_devices[m_last_id], &m_fifo[0], 12); + SCSIExecCommand(m_scsi_devices[m_last_id], &length); + SCSIGetPhase(m_scsi_devices[m_last_id], &phase); + + #if VERBOSE + printf("Command executed (id %d), new phase %d, length %x\n", m_last_id, phase, length); + #endif + + m_buffer_offset = m_buffer_size; + m_buffer_remaining = 0; + m_total_data = length; + + m_status &= ~7; // clear bus phases + m_status |= (phase & 7); // set the phase reported by the device +} + +void ncr539x_device::check_fifo_executable() +{ + if (get_cmd_len(m_fifo[0]) == m_fifo_ptr) + { + exec_fifo(); + } +} + +void ncr539x_device::fifo_write(UINT8 data) +{ + int phase = (m_status & 7); + + if (phase != SCSI_PHASE_DATAOUT) + { + #if VERBOSE + printf("539x: Write %02x @ FIFO[%x]\n", data, m_fifo_ptr); + #endif + m_fifo[m_fifo_ptr++] = data; + + if (m_selected) + { + check_fifo_executable(); + } + } + else // phase is DATAOUT + { + m_buffer[m_buffer_offset++] = data; + m_xfer_count--; + m_total_data--; + #if VERBOSE + printf("539x: Write %02x @ buffer[%x], xfer_count %x, total %x\n", data, m_buffer_offset-1, m_xfer_count, m_total_data); + #endif + + // default to flushing our entire buffer + int flush_size = m_buffer_size; + + // if the actual size is less than the buffer size, flush that instead + if (m_dma_size < m_buffer_size) + { + flush_size = m_dma_size; + } + + if ((m_buffer_offset == flush_size) || (m_xfer_count == 0)) + { + #if VERBOSE + printf("Flushing buffer to device, %x bytes left in buffer (%x total)\n", m_xfer_count, m_total_data); + #endif + SCSIWriteData(m_scsi_devices[m_last_id], m_buffer, flush_size); + m_buffer_offset = 0; + + // need a service request here too + m_irq_status = IRQ_STATUS_SERVICE_REQUEST; + m_status &= 7; + m_status |= MAIN_STATUS_INTERRUPT; + m_out_irq_func(ASSERT_LINE); + } + + if ((m_xfer_count == 0) && (m_total_data == 0)) + { + #if VERBOSE + printf("End of write, asserting service request\n"); + #endif + + m_buffer_offset = 0; + m_irq_status = IRQ_STATUS_SERVICE_REQUEST; + m_status = MAIN_STATUS_INTERRUPT | SCSI_PHASE_STATUS; + m_out_irq_func(ASSERT_LINE); + } + } +} + +void ncr539x_device::update_fifo_internal_state(int bytes) +{ + if (bytes >= 0x1f) + { + m_fifo_internal_state |= 0x1f; + } + else + { + m_fifo_internal_state &= ~0x1f; + m_fifo_internal_state |= (bytes & 0x1f); + } +} diff --git a/src/emu/machine/ncr539x.h b/src/emu/machine/ncr539x.h new file mode 100644 index 00000000000..eee6f560abf --- /dev/null +++ b/src/emu/machine/ncr539x.h @@ -0,0 +1,97 @@ +/* + * ncr5394/5396.h SCSI controller + * + */ + +#ifndef _NCR539x_H_ +#define _NCR539x_H_ + +#include "machine/scsidev.h" + +struct NCR539Xinterface +{ + const SCSIConfigTable *scsidevs; /* SCSI devices */ + devcb_write_line m_out_irq_cb; /* IRQ line */ + devcb_write_line m_out_drq_cb; /* DRQ line */ +}; + +// 539x registers +enum +{ +}; + +// device stuff +#define MCFG_NCR539X_ADD(_tag, _clock, _intrf) \ + MCFG_DEVICE_ADD(_tag, NCR539X, _clock) \ + MCFG_DEVICE_CONFIG(_intrf) + +class ncr539x_device : public device_t, + public NCR539Xinterface +{ +public: + // construction/destruction + ncr539x_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // our API + DECLARE_READ8_MEMBER(read); + DECLARE_WRITE8_MEMBER(write); + + void dma_read_data(int bytes, UINT8 *pData); + void dma_write_data(int bytes, UINT8 *pData); + + void *get_scsi_device(int id); + + void scan_devices(); +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual void device_stop(); + virtual void device_config_complete(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + +private: + void fifo_write(UINT8 data); + void check_fifo_executable(); + void exec_fifo(); + void update_fifo_internal_state(int bytes); + + SCSIInstance *m_scsi_devices[8]; + + UINT32 m_xfer_count; + UINT32 m_dma_size; + UINT8 m_command; + UINT8 m_last_id; + UINT8 m_timeout; + UINT8 m_sync_xfer_period; + UINT8 m_sync_offset; + UINT8 m_control1, m_control2, m_control3, m_control4; + UINT8 m_clock_factor; + UINT8 m_forced_test; + UINT8 m_data_alignment; + + bool m_selected; + bool m_chipid_available, m_chipid_lock; + + static const int m_fifo_size = 16; + UINT8 m_fifo_ptr, m_fifo[m_fifo_size]; + + int m_xfer_remaining; // amount in the FIFO when we're in data in phase + + // read-only registers + UINT8 m_status, m_irq_status, m_internal_state, m_fifo_internal_state; + + static const int m_buffer_size = 2048; + + UINT8 m_buffer[m_buffer_size]; + int m_buffer_offset, m_buffer_remaining, m_total_data; + + emu_timer *m_operation_timer; + + devcb_resolved_write_line m_out_irq_func; + devcb_resolved_write_line m_out_drq_func; +}; + +// device type definition +extern const device_type NCR539X; +#endif