summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices
diff options
context:
space:
mode:
author arbee <rb6502@users.noreply.github.com>2026-05-03 21:09:21 -0400
committer arbee <rb6502@users.noreply.github.com>2026-05-03 21:09:21 -0400
commitad3fd45a18261cf17171129d32a578e3abf1f0d3 (patch)
treed5c08422fb18000416e5eecf0b738cac556a736a /src/devices
parent132e657880f3efd2c98e5991b584a312ddef03d9 (diff)
machine/mb87030.cpp: Several bugfixes and a massive speedup [R. Belmont]
- DREQ was not always dropping at the end of a SCSI transaction. This was causing data to get blasted to DMA before the chip was set up in some cases. - The transfer counter could underflow when SCMD_TERM_MODE was in use. - SCMD_RST_OUT wasn't actually resetting anything. - Several delays were checked against the datasheet and found to be off by an order of magnitude. This was making SCSI-1 perform like floppy disks. - mpc2000xl and mpc3000 now have fully working SCSI. Both machines can format a disk, save and load data, and read CD-ROMs error-free. Previously they were struggling to even select a SCSI target, let alone send commands. - Additional testing: partitioned and formatted a hard disk on x68000 and I was able to load and run utilities from HDD on fz20m. This is your periodic reminder to make it easier for others to help you by providing software list images for computers/synths/samplers you write drivers for. And write setup instructions on wiki.mamedev.org.
Diffstat (limited to 'src/devices')
-rw-r--r--src/devices/machine/mb87030.cpp45
1 files changed, 34 insertions, 11 deletions
diff --git a/src/devices/machine/mb87030.cpp b/src/devices/machine/mb87030.cpp
index b23bd63039e..2aa6a6dd60d 100644
--- a/src/devices/machine/mb87030.cpp
+++ b/src/devices/machine/mb87030.cpp
@@ -13,6 +13,9 @@
//#define VERBOSE 1
#include "logmacro.h"
+// Transfer delays from the MB89352 datasheet. In clocks, which are 125 nanoseconds at 8 MHz.
+static constexpr int XFER_DATA_SETUP_CLKS = 2; // 2tCLF - 80 = 170 nanoseconds, so rounding up
+static constexpr int XFER_HANDSHAKE_CLKS = 1; // 10 nanoseconds on the datasheet, so 125 is very conservative
DEFINE_DEVICE_TYPE(MB87030, mb87030_device, "mb87030", "Fujitsu MB87030 SCSI controller")
DEFINE_DEVICE_TYPE(MB89351, mb89351_device, "mb89351", "Fujitsu MB89351 SCSI controller")
@@ -225,6 +228,7 @@ TIMER_CALLBACK_MEMBER(mb87030_device::bus_free_timeout)
void mb87030_device::scsi_command_complete()
{
LOG("%s\n", __FUNCTION__);
+ m_dreq_handler(false);
m_ints |= INTS_COMMAND_COMPLETE;
m_ssts &= ~(SSTS_SPC_BUSY|SSTS_XFER_IN_PROGRESS);
update_ints();
@@ -234,6 +238,7 @@ void mb87030_device::scsi_command_complete()
void mb87030_device::scsi_disconnect()
{
LOG("%s: m_tc %d\n", __FUNCTION__, m_tc);
+ m_dreq_handler(false);
m_ssts &= ~(SSTS_INIT_CONNECTED|SSTS_TARG_CONNECTED|SSTS_SPC_BUSY|SSTS_XFER_IN_PROGRESS);
m_ints = INTS_DISCONNECTED;
update_ints();
@@ -400,6 +405,7 @@ void mb87030_device::step(bool timeout)
if (m_scsi_phase != (ctrl & S_PHASE_MASK)) {
LOG("SCSI phase change during transfer\n");
+ m_dreq_handler(false);
m_ints |= INTS_SERVICE_REQUIRED;
m_ssts &= ~SSTS_SPC_BUSY;
m_scsi_bus->data_w(m_scsi_refid, 0);
@@ -418,14 +424,14 @@ void mb87030_device::step(bool timeout)
}
update_state((ctrl & S_INP) ? State::TransferRecvData : State::TransferSendData);
- if (m_dma_transfer && m_tc && !(ctrl & S_INP) && !m_fifo.full())
+ if (m_dma_transfer && m_tc && !(ctrl & S_INP) && (m_tc > m_fifo.queue_length()))
m_dreq_handler(true);
step(false);
break;
case State::TransferRecvData:
if (!m_tc && (m_scmd & SCMD_TERM_MODE)) {
- update_state(State::TransferSendAck, 10);
+ update_state(State::TransferSendAck, XFER_DATA_SETUP_CLKS);
break;
}
@@ -456,13 +462,13 @@ void mb87030_device::step(bool timeout)
if (m_tc && !m_fifo.empty()) {
LOG("pulling write data: %02X (%d left)\n", data, m_fifo.queue_length() - 1);
m_scsi_bus->data_w(m_scsi_refid, m_fifo.dequeue());
- update_state(State::TransferSendAck, 10);
+ update_state(State::TransferSendAck, XFER_DATA_SETUP_CLKS);
break;
}
if (!m_tc && (m_scmd & SCMD_TERM_MODE)) {
m_scsi_bus->data_w(m_scsi_refid, m_temp);
- update_state(State::TransferSendAck, 10);
+ update_state(State::TransferSendAck, XFER_DATA_SETUP_CLKS);
break;
}
break;
@@ -473,20 +479,24 @@ void mb87030_device::step(bool timeout)
scsi_set_ctrl(S_ACK, S_ACK);
m_scsi_bus->ctrl_wait(m_scsi_refid, 0, S_REQ);
- update_state(State::TransferWaitDeassertREQ, 10);
+ update_state(State::TransferWaitDeassertREQ, XFER_HANDSHAKE_CLKS);
break;
case State::TransferWaitDeassertREQ:
if (!(ctrl & S_REQ))
- update_state(State::TransferDeassertACK, 10);
+ update_state(State::TransferDeassertACK, XFER_HANDSHAKE_CLKS); // minimum is 35 ns, or 3.5 clocks at 8 MHz
break;
- case State::TransferDeassertACK:
- m_tc--;
+ case State::TransferDeassertACK: {
+ const bool is_terminal_byte = !m_tc && (m_scmd & SCMD_TERM_MODE);
+
+ if (!is_terminal_byte)
+ m_tc--;
+
if(m_tc)
- update_state(State::TransferWaitReq, 10);
+ update_state(State::TransferWaitReq, XFER_HANDSHAKE_CLKS);
else
- update_state(State::TransferWaitFifoEmpty, 10);
+ update_state(State::TransferWaitFifoEmpty, XFER_HANDSHAKE_CLKS);
m_scsi_bus->ctrl_wait(m_scsi_refid, S_REQ, S_REQ);
// deassert ATN after last byte of message out phase
@@ -495,6 +505,7 @@ void mb87030_device::step(bool timeout)
// deassert ACK except for last byte of message in phase
else if (m_tc || (ctrl & S_PHASE_MASK) != S_PHASE_MSG_IN)
scsi_set_ctrl(0, S_ACK);
+ }
break;
case State::TransferWaitFifoEmpty:
@@ -564,7 +575,9 @@ void mb87030_device::update_ssts()
else
m_ssts &= ~SSTS_DREQ_EMPTY;
- if (m_fifo.full())
+ const bool dma_xfer = m_dma_transfer && (m_ssts & SSTS_XFER_IN_PROGRESS);
+
+ if (!dma_xfer && m_fifo.full())
m_ssts |= SSTS_DREQ_FULL;
else
m_ssts &= ~SSTS_DREQ_FULL;
@@ -621,6 +634,16 @@ void mb87030_device::scmd_w(uint8_t data)
scsi_set_ctrl((m_scmd & SCMD_RST_OUT) ? S_RST : 0, S_RST);
}
+ if (m_scmd & SCMD_RST_OUT) {
+ m_dreq_handler(false);
+ m_fifo.clear();
+ m_scsi_bus->data_w(m_scsi_refid, 0);
+ m_ssts &= ~(SSTS_INIT_CONNECTED | SSTS_TARG_CONNECTED | SSTS_SPC_BUSY | SSTS_XFER_IN_PROGRESS);
+ update_state(State::Idle);
+ update_ints();
+ return;
+ }
+
switch (m_scmd & SCMD_CMD_MASK) {
case SCMD_CMD_BUS_RELEASE:
LOG("%s: Bus release\n", __FUNCTION__);