summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/z80sio.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/z80sio.cpp')
-rw-r--r--src/devices/machine/z80sio.cpp504
1 files changed, 292 insertions, 212 deletions
diff --git a/src/devices/machine/z80sio.cpp b/src/devices/machine/z80sio.cpp
index e83907157cb..5a26319f7b4 100644
--- a/src/devices/machine/z80sio.cpp
+++ b/src/devices/machine/z80sio.cpp
@@ -21,13 +21,13 @@
6MHz 1200Kbps
10MHz 2500Kbps
-- Asynchrounous features -------------------------------------------
- 5-8 bit per char Y
- 1,1.5,2 stop bits Y
- odd/even parity Y
- x1,x16,x32,x64 Y
+ * 5-8 bit per char Y
+ * 1,1.5,2 stop bits Y
+ * odd/even parity Y
+ * x1,x16,x32,x64 Y
break det/gen Y
- parity, framing & Y
- overrun error det Y
+ * parity, framing & Y
+ overrun error det Y
-- Byte oriented synchrounous features -------------------------------
Int/ext char sync Y
1/2 synch chars Y
@@ -41,8 +41,8 @@
1-fld resid hand Y
Valid rec msg protection Y
--
- Receiver FIFO 3
- Transmitter FIFO 1
+ * Receiver FIFO 3
+ * Transmitter FIFO 1
-------------------------------------------------------------------------
* = Features that has been implemented n/a = features that will not
***************************************************************************/
@@ -54,38 +54,40 @@
// MACROS / CONSTANTS
//**************************************************************************
-#define LOG_GENERAL 0x001
-#define LOG_SETUP 0x002
-#define LOG_PRINTF 0x004
-#define LOG_READ 0x008
-#define LOG_INT 0x010
-#define LOG_TX 0x020
-#define LOG_RCV 0x040
-#define LOG_CTS 0x080
-#define LOG_DCD 0x100
-
-#define VERBOSE 0 //(LOG_PRINTF | LOG_SETUP | LOG_GENERAL)
-
-#define LOGMASK(mask, ...) do { if (VERBOSE & mask) logerror(__VA_ARGS__); } while (0)
-#define LOGLEVEL(mask, level, ...) do { if ((VERBOSE & mask) >= level) logerror(__VA_ARGS__); } while (0)
-
-#define LOG(...) LOGMASK(LOG_GENERAL, __VA_ARGS__)
-#define LOGR(...) LOGMASK(LOG_READ, __VA_ARGS__)
-#define LOGSETUP(...) LOGMASK(LOG_SETUP, __VA_ARGS__)
-#define LOGINT(...) LOGMASK(LOG_INT, __VA_ARGS__)
-#define LOGTX(...) LOGMASK(LOG_TX, __VA_ARGS__)
-#define LOGRCV(...) LOGMASK(LOG_RCV, __VA_ARGS__)
-#define LOGCTS(...) LOGMASK(LOG_CTS, __VA_ARGS__)
-#define LOGDCD(...) LOGMASK(LOG_DCD, __VA_ARGS__)
-
-#if VERBOSE & LOG_PRINTF
-#define logerror printf
-#endif
+//#define LOG_GENERAL (1U << 0)
+#define LOG_SETUP (1U << 1)
+#define LOG_READ (1U << 2)
+#define LOG_INT (1U << 3)
+#define LOG_CMD (1U << 4)
+#define LOG_TX (1U << 5)
+#define LOG_RCV (1U << 6)
+#define LOG_CTS (1U << 7)
+#define LOG_DCD (1U << 8)
+#define LOG_SYNC (1U << 9)
+#define LOG_BIT (1U << 10)
+
+//#define VERBOSE (LOG_INT|LOG_READ|LOG_SETUP|LOG_TX|LOG_CMD) //(LOG_SETUP|LOG_INT|LOG_CMD|LOG_DCD|LOG_CTS|LOG_TX)
+//#define LOG_OUTPUT_FUNC printf
+
+#include "logmacro.h"
+
+#define LOGSETUP(...) LOGMASKED(LOG_SETUP, __VA_ARGS__)
+#define LOGR(...) LOGMASKED(LOG_READ, __VA_ARGS__)
+#define LOGINT(...) LOGMASKED(LOG_INT, __VA_ARGS__)
+#define LOGCMD(...) LOGMASKED(LOG_CMD, __VA_ARGS__)
+#define LOGTX(...) LOGMASKED(LOG_TX, __VA_ARGS__)
+#define LOGRCV(...) LOGMASKED(LOG_RCV, __VA_ARGS__)
+#define LOGCTS(...) LOGMASKED(LOG_CTS, __VA_ARGS__)
+#define LOGDCD(...) LOGMASKED(LOG_DCD, __VA_ARGS__)
+#define LOGSYNC(...) LOGMASKED(LOG_SYNC, __VA_ARGS__)
+#define LOGBIT(...) LOGMASKED(LOG_BIT, __VA_ARGS__)
#ifdef _MSC_VER
#define FUNCNAME __func__
+#define LLFORMAT "%I64d"
#else
#define FUNCNAME __PRETTY_FUNCTION__
+#define LLFORMAT "%lld"
#endif
#define CHANA_TAG "cha"
@@ -99,14 +101,14 @@
const device_type Z80SIO = device_creator<z80sio_device>;
const device_type Z80SIO_CHANNEL = device_creator<z80sio_channel>;
const device_type UPD7201N = device_creator<upd7201N_device>; // Remove trailing N when z80dart.cpp's 7201 implementation is fully replaced
+const device_type I8274N = device_creator<i8274N_device>; // Remove trailing N when z80dart.cpp's 8274 implementation is fully replaced
//-------------------------------------------------
// device_mconfig_additions -
//-------------------------------------------------
-
MACHINE_CONFIG_FRAGMENT( z80sio )
-MCFG_DEVICE_ADD(CHANA_TAG, Z80SIO_CHANNEL, 0)
-MCFG_DEVICE_ADD(CHANB_TAG, Z80SIO_CHANNEL, 0)
+ MCFG_DEVICE_ADD(CHANA_TAG, Z80SIO_CHANNEL, 0)
+ MCFG_DEVICE_ADD(CHANB_TAG, Z80SIO_CHANNEL, 0)
MACHINE_CONFIG_END
machine_config_constructor z80sio_device::device_mconfig_additions() const
@@ -121,7 +123,6 @@ machine_config_constructor z80sio_device::device_mconfig_additions() const
//-------------------------------------------------
// z80sio_device - constructor
//-------------------------------------------------
-
z80sio_device::z80sio_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, uint32_t clock, uint32_t variant, const char *shortname, const char *source)
: device_t(mconfig, type, name, tag, owner, clock, shortname, source),
device_z80daisy_interface(mconfig, *this),
@@ -146,7 +147,8 @@ z80sio_device::z80sio_device(const machine_config &mconfig, device_type type, co
m_out_txdrqa_cb(*this),
m_out_rxdrqb_cb(*this),
m_out_txdrqb_cb(*this),
- m_variant(variant)
+ m_variant(variant),
+ m_cputag("maincpu")
{
for (auto & elem : m_int_state)
elem = 0;
@@ -176,7 +178,8 @@ z80sio_device::z80sio_device(const machine_config &mconfig, const char *tag, dev
m_out_txdrqa_cb(*this),
m_out_rxdrqb_cb(*this),
m_out_txdrqb_cb(*this),
- m_variant(TYPE_Z80SIO)
+ m_variant(TYPE_Z80SIO),
+ m_cputag("maincpu")
{
for (auto & elem : m_int_state)
elem = 0;
@@ -185,6 +188,9 @@ z80sio_device::z80sio_device(const machine_config &mconfig, const char *tag, dev
upd7201N_device::upd7201N_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: z80sio_device(mconfig, UPD7201N, "UPD 7201", tag, owner, clock, TYPE_UPD7201, "upd7201n", __FILE__){ }
+i8274N_device::i8274N_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : z80sio_device(mconfig, I8274N, "i8274", tag, owner, clock, TYPE_I8274, "i8274n", __FILE__){ }
+
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
@@ -218,6 +224,7 @@ void z80sio_device::device_start()
// state saving
save_item(NAME(m_int_state));
+ save_item(NAME(m_int_source));
}
@@ -238,23 +245,21 @@ void z80sio_device::device_reset()
int z80sio_device::z80daisy_irq_state()
{
int state = 0;
- int i;
-
- LOGINT("%s %s A:%d%d%d%d B:%d%d%d%d ",FUNCNAME, tag(),
- m_int_state[0], m_int_state[1], m_int_state[2], m_int_state[3],
- m_int_state[4], m_int_state[5], m_int_state[6], m_int_state[7]);
+ LOGINT("%s %s Hi->Lo:%d%d%d%d%d%d ", tag(), FUNCNAME,
+ m_int_state[0], m_int_state[1], m_int_state[2],
+ m_int_state[3], m_int_state[4], m_int_state[5]);
// loop over all interrupt sources
- for (i = 0; i < 8; i++)
+ for (auto & elem : m_int_state)
{
// if we're servicing a request, don't indicate more interrupts
- if (m_int_state[i] & Z80_DAISY_IEO)
+ if (elem & Z80_DAISY_IEO)
{
state |= Z80_DAISY_IEO;
break;
}
- state |= m_int_state[i];
+ state |= elem;
}
LOGINT("Interrupt State %u\n", state);
@@ -268,30 +273,29 @@ int z80sio_device::z80daisy_irq_state()
//-------------------------------------------------
int z80sio_device::z80daisy_irq_ack()
{
- int i;
-
- LOGINT("%s %s \n",FUNCNAME, tag());
+ // default irq vector is -1 for 68000 but 0 for z80 for example...
+ int ret = owner()->subdevice<cpu_device>(m_cputag)->default_irq_vector();
+ LOGINT("%s %s \n",tag(), FUNCNAME);
// loop over all interrupt sources
- for (i = 0; i < 8; i++)
+ for (auto & elem : m_int_state)
{
// find the first channel with an interrupt requested
- if (m_int_state[i] & Z80_DAISY_INT)
+ if (elem & Z80_DAISY_INT)
{
- // clear interrupt, switch to the IEO state, and update the IRQs
- m_int_state[i] = Z80_DAISY_IEO;
+ elem = Z80_DAISY_IEO; // Set IUS bit (called IEO in z80 daisy lingo)
m_chanA->m_rr0 &= ~z80sio_channel::RR0_INTERRUPT_PENDING;
+ LOGINT(" - Found an INT request, ");
+ LOGINT("returning RR2: %02x\n", m_chanB->m_rr2 );
check_interrupts();
-
- //LOG("%s %s \n",FUNCNAME, tag(), m_chanB->m_rr2);
-
return m_chanB->m_rr2;
}
}
+ ret = m_chanB->m_rr2;
+ LOGINT(" - failed to find an interrupt to ack, returning default IRQ vector: %02x\n", ret );
+ logerror("z80sio_irq_ack: failed to find an interrupt to ack!\n");
- //logerror("z80sio_irq_ack: failed to find an interrupt to ack!\n");
-
- return m_chanB->m_rr2;
+ return ret;
}
@@ -300,24 +304,27 @@ int z80sio_device::z80daisy_irq_ack()
//-------------------------------------------------
void z80sio_device::z80daisy_irq_reti()
{
- int i;
+ LOGINT("%s %s \n",tag(), FUNCNAME);
- LOGINT("%s %s \n",FUNCNAME, tag());
+ if((m_variant == TYPE_I8274) || (m_variant == TYPE_UPD7201))
+ {
+ LOGINT(" - I8274 and UPD7201 lacks RETI detection, no action taken\n");
+ return;
+ }
// loop over all interrupt sources
- for (i = 0; i < 8; i++)
+ for (auto & elem : m_int_state)
{
- // find the first channel with an IEO pending
- if (m_int_state[i] & Z80_DAISY_IEO)
+ // find the first channel with an interrupt requested
+ if (elem & Z80_DAISY_IEO)
{
// clear the IEO state and update the IRQs
- m_int_state[i] &= ~Z80_DAISY_IEO;
+ elem &= ~Z80_DAISY_IEO;
check_interrupts();
return;
}
}
-
- //logerror("z80sio_irq_reti: failed to find an interrupt to clear IEO on!\n");
+ LOGINT("z80sio_irq_reti: failed to find an interrupt to clear IEO on!\n");
}
@@ -347,66 +354,94 @@ void z80sio_device::reset_interrupts()
check_interrupts();
}
-
-//-------------------------------------------------
-// trigger_interrupt - TODO: needs attention for SIO
-//-------------------------------------------------
-void z80sio_device::trigger_interrupt(int index, int state)
+int z80sio_device::get_interrupt_prio(int index, int type)
{
- uint8_t vector = m_chanB->m_wr2;
- int priority;
+ int prio_level = -1;
+ int priority = -1;
- LOGINT("%s %s \n",FUNCNAME, tag());
-
-#if 0
- if((m_variant == TYPE_I8274) || (m_variant == TYPE_UPD7201))
+ if ((m_variant == TYPE_I8274) || (m_variant == TYPE_UPD7201))
{
- int prio_level = 0;
- switch(state)
+ /* These CPU variants use Bit 2 of WR2 of Channnel A to determine the priority Hi to Lo:
+ 0: RxA TxA RxB TxB ExtA ExtB
+ 1: RxA RxB TxA TxB ExtA ExtB */
+ switch(type)
{
- case z80sio_channel::INT_TRANSMIT:
- prio_level = 1;
- break;
- case z80sio_channel::INT_RECEIVE:
- case z80sio_channel::INT_SPECIAL:
- prio_level = 0;
- break;
- case z80sio_channel::INT_EXTERNAL:
- prio_level = 2;
- break;
+ case z80sio_channel::INT_RECEIVE:
+ case z80sio_channel::INT_SPECIAL: prio_level = z80sio_channel::INT_RCV_SPC_PRI_LVL; break; // 0
+ case z80sio_channel::INT_TRANSMIT: prio_level = z80sio_channel::INT_TRANSMIT_PRI_LVL; break; // 1
+ case z80sio_channel::INT_EXTERNAL: prio_level = z80sio_channel::INT_EXTERNAL_PRI_LVL; break; // 2
+ default:
+ logerror("Bad interrupt source being prioritized!");
+ return -1;
}
+ // Assume that the PRIORITY bit is set
+ priority = (prio_level * 2) + index;
- if(m_chanA->m_wr2 & z80sio_channel::WR2_PRIORITY)
+ // Check if it actually was cleared
+ if ( (m_chanA->m_wr2 & z80sio_channel::WR2_PRIORITY) == 0)
{
- priority = (prio_level * 2) + index;
- }
- else
- {
- priority = (prio_level == 2) ? index + 4 : ((index * 2) + prio_level);
+ // Adjust priority if needed, only affects TxA and RxB
+ if (index == CHANNEL_A && type == z80sio_channel::INT_TRANSMIT )
+ priority--;
+ else if (index == CHANNEL_B && type == z80sio_channel::INT_RECEIVE )
+ priority++;
}
+ }
+ else // Plain old z80sio
+ {
+ priority = (index << 2) | type;
+ }
+ return priority;
+}
+
+/*
+ 8274: "RR2 contains the vector which gets modified to indicate the source of interrupt. However, the state of
+ the vector does not change if no new interrupts are generated. The contents of RR2 are only changed when
+ a new interrupt is generated. In order to get the correct information, RR2 must be read only after an
+ interrrupt is generated, otherwise it will indicate the previous state."
+ 8274: "If RR2 is specified but not read, no internal interrupts, regardless of priority, are accepted."
+*/
+uint8_t z80sio_device::modify_vector(int index, int type)
+{
+ uint8_t vector = m_chanB->m_wr2;
+ if((m_variant == TYPE_I8274) || (m_variant == TYPE_UPD7201))
+ {
if (m_chanB->m_wr1 & z80sio_channel::WR1_STATUS_VECTOR)
{
- vector = (!index << 2) | state;
+ vector = (!index << 2) | type;
if((m_chanA->m_wr1 & 0x18) == z80sio_channel::WR2_MODE_8086_8088)
{
- vector = (m_chanB->m_wr2 & 0xf8) | vector;
+ vector = (m_chanB->m_wr2 & 0xf8) | vector; // m_chanB->m_wr2;
}
else
{
- vector = (m_chanB->m_wr2 & 0xe3) | (vector << 2);
+ vector = (m_chanB->m_wr2 & 0xe3) | (vector << 2); //(m_chanB->m_wr2 << 2);
}
}
}
else
{
-#endif
- priority = (index << 2) | state;
if (m_chanB->m_wr1 & z80sio_channel::WR1_STATUS_VECTOR)
{
// status affects vector
- vector = (m_chanB->m_wr2 & 0xf1) | (!index << 3) | (state << 1);
+ vector = (m_chanB->m_wr2 & 0xf1) | (!index << 3) | (type << 1);
}
-// }
+ }
+ return vector;
+}
+
+//-------------------------------------------------
+// trigger_interrupt - TODO: needs attention for SIO
+//-------------------------------------------------
+void z80sio_device::trigger_interrupt(int index, int type)
+{
+ uint8_t priority = get_interrupt_prio(index, type);
+ uint8_t vector = modify_vector(index, type);
+
+ LOGINT("%s %s Chan:%c Type:%s\n", tag(), FUNCNAME, 'A' + index, std::array<char const *, 4>
+ {{"INT_TRANSMIT", "INT_EXTERNAL", "INT_RECEIVE", "INT_SPECIAL"}}[type]);
+ LOGINT(" - Priority:%02x Vector:%02x\n", priority, vector);
+
// update vector register
m_chanB->m_rr2 = vector;
@@ -414,6 +449,9 @@ void z80sio_device::trigger_interrupt(int index, int state)
m_int_state[priority] |= Z80_DAISY_INT;
m_chanA->m_rr0 |= z80sio_channel::RR0_INTERRUPT_PENDING;
+ // remember the source and channel
+ m_int_source[priority] = (type & 0xff) | (index << 8);
+
// check for interrupt
check_interrupts();
}
@@ -425,7 +463,10 @@ void z80sio_device::trigger_interrupt(int index, int state)
int z80sio_device::m1_r()
{
LOGINT("%s %s \n",FUNCNAME, tag());
- return z80daisy_irq_ack();
+ if((m_variant == TYPE_I8274) || (m_variant == TYPE_UPD7201))
+ return 0;
+ else
+ return z80daisy_irq_ack();
}
@@ -438,8 +479,6 @@ READ8_MEMBER( z80sio_device::cd_ba_r )
int cd = BIT(offset, 1);
z80sio_channel *channel = ba ? m_chanB : m_chanA;
- //LOG("%s %s %c %s read\n",FUNCNAME, tag(), 'A' + ba ? 1 : 0 , cd ? "control" : "data" );
-
return cd ? channel->control_read() : channel->data_read();
}
@@ -453,8 +492,6 @@ WRITE8_MEMBER( z80sio_device::cd_ba_w )
int cd = BIT(offset, 1);
z80sio_channel *channel = ba ? m_chanB : m_chanA;
- LOG("%s %s %c %s write\n",FUNCNAME, tag(), 'A' + ba ? 1 : 0 , cd ? "control" : "data" );
-
if (cd)
channel->control_write(data);
else
@@ -471,8 +508,6 @@ READ8_MEMBER( z80sio_device::ba_cd_r )
int cd = BIT(offset, 0);
z80sio_channel *channel = ba ? m_chanB : m_chanA;
- //LOG("%s %s %c %s read\n",FUNCNAME, tag(), 'A' + ba ? 1 : 0 , cd ? "control" : "data" );
-
return cd ? channel->control_read() : channel->data_read();
}
@@ -486,8 +521,6 @@ WRITE8_MEMBER( z80sio_device::ba_cd_w )
int cd = BIT(offset, 0);
z80sio_channel *channel = ba ? m_chanB : m_chanA;
- LOG("%s %s %c %s write\n",FUNCNAME, tag(), 'A' + ba ? 1 : 0 , cd ? "control" : "data" );
-
if (cd)
channel->control_write(data);
else
@@ -505,7 +538,6 @@ z80sio_channel::z80sio_channel(const machine_config &mconfig, const char *tag, d
: device_t(mconfig, Z80SIO_CHANNEL, "Z80 SIO channel", tag, owner, clock, "z80sio_channel", __FILE__),
device_serial_interface(mconfig, *this),
m_rx_error(0),
- m_rx_fifo(-1),
m_rx_clock(0),
m_rx_first(0),
m_rx_break(0),
@@ -524,12 +556,6 @@ z80sio_channel::z80sio_channel(const machine_config &mconfig, const char *tag, d
// Reset all registers
m_rr0 = m_rr1 = m_rr2 = 0;
m_wr0 = m_wr1 = m_wr2 = m_wr3 = m_wr4 = m_wr5 = m_wr6 = m_wr7 = 0;
-
- for (int i = 0; i < 3; i++)
- {
- m_rx_data_fifo[i] = 0;
- m_rx_error_fifo[i] = 0;
- }
}
@@ -555,10 +581,7 @@ void z80sio_channel::device_start()
save_item(NAME(m_wr5));
save_item(NAME(m_wr6));
save_item(NAME(m_wr7));
- save_item(NAME(m_rx_data_fifo));
- save_item(NAME(m_rx_error_fifo));
save_item(NAME(m_rx_error));
- save_item(NAME(m_rx_fifo));
save_item(NAME(m_rx_clock));
save_item(NAME(m_rx_first));
save_item(NAME(m_rx_break));
@@ -618,7 +641,7 @@ void z80sio_channel::tra_callback()
{
if (!(m_wr5 & WR5_TX_ENABLE))
{
- LOG("%s() \"%s \"Channel %c transmit mark 1 m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5);
+ LOGBIT("%s() \"%s \"Channel %c transmit mark 1 m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5);
// transmit mark
if (m_index == z80sio_device::CHANNEL_A)
m_uart->m_out_txda_cb(1);
@@ -627,7 +650,7 @@ void z80sio_channel::tra_callback()
}
else if (m_wr5 & WR5_SEND_BREAK)
{
- LOG("%s() \"%s \"Channel %c send break 1 m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5);
+ LOGBIT("%s() \"%s \"Channel %c send break 1 m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5);
// transmit break
if (m_index == z80sio_device::CHANNEL_A)
m_uart->m_out_txda_cb(0);
@@ -638,7 +661,7 @@ void z80sio_channel::tra_callback()
{
int db = transmit_register_get_data_bit();
- LOG("%s() \"%s \"Channel %c transmit data bit %d m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, db, m_wr5);
+ LOGBIT("%s() \"%s \"Channel %c transmit data bit %d m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, db, m_wr5);
// transmit data
if (m_index == z80sio_device::CHANNEL_A)
m_uart->m_out_txda_cb(db);
@@ -647,7 +670,7 @@ void z80sio_channel::tra_callback()
}
else
{
- LOG("%s() \"%s \"Channel %c Failed to transmit m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5);
+ LOGBIT("%s() \"%s \"Channel %c Failed to transmit m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5);
logerror("%s \"%s \"Channel %c Failed to transmit\n", FUNCNAME, m_owner->tag(), 'A' + m_index);
}
}
@@ -661,7 +684,7 @@ void z80sio_channel::tra_complete()
LOG("%s %s\n",FUNCNAME, tag());
if ((m_wr5 & WR5_TX_ENABLE) && !(m_wr5 & WR5_SEND_BREAK) && !(m_rr0 & RR0_TX_BUFFER_EMPTY))
{
- LOG("%s() \"%s \"Channel %c Transmit Data Byte '%02x' m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_tx_data, m_wr5);
+ LOGTX("%s() \"%s \"Channel %c Transmit Data Byte '%02x' m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_tx_data, m_wr5);
transmit_register_setup(m_tx_data);
@@ -673,7 +696,7 @@ void z80sio_channel::tra_complete()
}
else if (m_wr5 & WR5_SEND_BREAK)
{
- LOG("%s() \"%s \"Channel %c Transmit Break 0 m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5);
+ LOGTX("%s() \"%s \"Channel %c Transmit Break 0 m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5);
// transmit break
if (m_index == z80sio_device::CHANNEL_A)
m_uart->m_out_txda_cb(0);
@@ -682,7 +705,7 @@ void z80sio_channel::tra_complete()
}
else
{
- LOG("%s() \"%s \"Channel %c Transmit Mark 1 m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5);
+ LOGTX("%s() \"%s \"Channel %c Transmit Mark 1 m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5);
// transmit mark
if (m_index == z80sio_device::CHANNEL_A)
m_uart->m_out_txda_cb(1);
@@ -693,7 +716,7 @@ void z80sio_channel::tra_complete()
// if transmit buffer is empty
if (m_rr0 & RR0_TX_BUFFER_EMPTY)
{
- LOG("%s() \"%s \"Channel %c Transmit buffer empty m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5);
+ LOGTX("%s() \"%s \"Channel %c Transmit buffer empty m_wr5:%02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5);
// then all characters have been sent
m_rr1 |= RR1_ALL_SENT;
@@ -711,16 +734,9 @@ void z80sio_channel::rcv_callback()
{
if (m_wr3 & WR3_RX_ENABLE)
{
- LOG("%s() \"%s \"Channel %c Received Data Bit %d\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_rxd);
+ LOGBIT("%s() \"%s \"Channel %c Received Data Bit %d\n", FUNCNAME, m_owner->tag(), 'A' + m_index, m_rxd);
receive_register_update_bit(m_rxd);
}
-#if 0
- else
- {
- LOG("%s() \"%s \"Channel %c Received Data Bit but receiver is disabled\n", FUNCNAME, m_owner->tag(), 'A' + m_index);
- logerror("Z80SIO %s() \"%s \"Channel %c Received data dit but receiver is disabled\n", __func__, m_owner->tag(), 'A' + m_index);
- }
-#endif
}
@@ -733,7 +749,7 @@ void z80sio_channel::rcv_complete()
receive_register_extract();
data = get_received_char();
- LOG("%s() \"%s \"Channel %c Received Data %02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, data);
+ LOGRCV("%s() \"%s \"Channel %c Received Data %02x\n", FUNCNAME, m_owner->tag(), 'A' + m_index, data);
receive_data(data);
}
@@ -741,7 +757,6 @@ void z80sio_channel::rcv_complete()
//-------------------------------------------------
// get_clock_mode - get clock divisor
//-------------------------------------------------
-
int z80sio_channel::get_clock_mode()
{
//LOG("%s %s\n",FUNCNAME, tag());
@@ -758,17 +773,16 @@ int z80sio_channel::get_clock_mode()
return clocks;
}
-/* From "uPD7201/7201A MULTI PROTOCOL SERIAL COMMUNICATION CONTROLLER" by NEC:
-"RTSA (Request to Send A): The state of the RTS bit (01 of the CR5 register) controls this pin. If
-the RTS bit is reset in the asynchronous mode, a high level will not be output on the RTS pin until
-all transmit characters are written and the all sent bit (D0 of the SR1 register) is set. In the
-synchronous mode, the state of the RTS bit is used as is. That is, when the RTS bit is 0, the RTS
-pin is 1. When the RTS bit is 1, the RTS pin is O."
-
-CR5 = m_wr5 and SR1 = m_rr1
-
+/*
+ From "uPD7201/7201A MULTI PROTOCOL SERIAL COMMUNICATION CONTROLLER" by NEC:
+ "RTSA (Request to Send A): The state of the RTS bit (01 of the CR5 register) controls this pin. If
+ the RTS bit is reset in the asynchronous mode, a high level will not be output on the RTS pin until
+ all transmit characters are written and the all sent bit (D0 of the SR1 register) is set. In the
+ synchronous mode, the state of the RTS bit is used as is. That is, when the RTS bit is 0, the RTS
+ pin is 1. When the RTS bit is 1, the RTS pin is O."
+
+ CR5 = m_wr5 and SR1 = m_rr1
*/
-
void z80sio_channel::set_rts(int state)
{
LOG("%s(%d) \"%s\" Channel %c \n", FUNCNAME, state, m_owner->tag(), 'A' + m_index);
@@ -801,7 +815,6 @@ void z80sio_channel::update_rts()
//-------------------------------------------------
// get_stop_bits - get number of stop bits
//-------------------------------------------------
-
device_serial_interface::stop_bits_t z80sio_channel::get_stop_bits()
{
LOG("%s %s\n",FUNCNAME, tag());
@@ -819,7 +832,6 @@ device_serial_interface::stop_bits_t z80sio_channel::get_stop_bits()
//-------------------------------------------------
// get_rx_word_length - get receive word length
//-------------------------------------------------
-
int z80sio_channel::get_rx_word_length()
{
LOG("%s %s\n",FUNCNAME, tag());
@@ -840,7 +852,6 @@ int z80sio_channel::get_rx_word_length()
//-------------------------------------------------
// get_tx_word_length - get transmit word length
//-------------------------------------------------
-
int z80sio_channel::get_tx_word_length()
{
LOG("%s %s\n",FUNCNAME, tag());
@@ -863,37 +874,86 @@ int z80sio_channel::get_tx_word_length()
* Break/Abort latch. */
uint8_t z80sio_channel::do_sioreg_rr0()
{
- LOG("%s %s\n",FUNCNAME, tag());
+ LOGR("%s %s\n",FUNCNAME, tag());
return m_rr0;
}
+
/*
* This register contains the Special Receive condition status bits and Residue
* codes for the I-Field in the SDLC Receive Mode. */
uint8_t z80sio_channel::do_sioreg_rr1()
{
- LOG("%s %s\n",FUNCNAME, tag());
+ LOGR("%s %s\n",FUNCNAME, tag());
+ // channel B only, channel A returns 0
+ if (m_index == z80sio_device::CHANNEL_A) return 0;
+
return m_rr1;
}
-/*
- * This register contains the interrupt vector written into WR2 if the Status
-Affects Vector control bit is not set. If the control bit is set, it contains the
-modified vector listed in the Status Affects Vector paragraph of the Write
-Register 1 section. When this register is read, the vector returned is modi-
-fied by the highest priority interrupting condition at the time of the read. If
-no interrupts are pending, the vector is modified with V3 = 0, V2 = 1, and
-V1 = 1. This register is read only through Channel B. */
+
+/* Z80-SIO Technical Manual: "This register contains the interrupt vector
+ written into WR2 if the Status Affects Vector control bit is not set.
+ If the control bit is set, it contains the modified vector listed in
+ the Status Affects Vector paragraph of the Write Register 1 section.
+ When this register is read, the vector returned is modified by the
+ highest priority interrupting condition at the time of the read. If
+ no interrupts are pending, the vector is modified with V3 = 0, V2 = 1, and
+ V1 = 1. This register is read only through Channel B."
+
+ Intel 8274 datasheet: "RR2 - Channel B: Interrupt Vector - Contains the interrupt
+ vector programmed in into WR2. If the status affects vector mode is selected (WR1:D2),
+ it containes the modified vector for the highest priority interrupt pending.
+ If no interrupts are pending the variable bits in the vector are set to one."
+
+ NEC upd7201 MPSC2 Technical Manual: "When the MPSC2 is used in vectored mode, the
+ contents of this register are placed on the bus during the appropriate portion of
+ interrupt acknowledge sequence. You can read the value of CR2B at any time.
+ This is particularly useful in determining the cause of an interrup when using the
+ MPSC2 in Non-vectored mode."
+*/
uint8_t z80sio_channel::do_sioreg_rr2()
{
- LOG("%s %s\n",FUNCNAME, tag());
- // channel B only
- return m_index == z80sio_device::CHANNEL_B ? m_rr2 : 0;
+ LOGINT("%s %s Chan:%c\n", tag(), FUNCNAME, 'A' + m_index);
+ // channel B only, channel A returns 0
+ if (m_index == z80sio_device::CHANNEL_A) return 0;
+
+ LOGINT(" - Channel B so we might need to update the vector modification\n");
+ // Assume the unmodified vector
+ m_rr2 = m_uart->m_chanB->m_wr2;
+
+ if((m_variant == z80sio_device::TYPE_I8274) || (m_variant == z80sio_device::TYPE_UPD7201))
+ {
+ int i = 0;
+ LOGINT(" - 8274 or 7201 requires special care\n");
+
+ // loop over all interrupt sources
+ for (auto & elem : m_uart->m_int_state)
+ {
+ // find the first channel with an interrupt requested
+ if (elem & Z80_DAISY_INT)
+ {
+ LOGINT(" - Checking an INT source %d\n", i);
+ m_rr2 = m_uart->modify_vector((m_uart->m_int_source[i] >> 8) & 1, m_uart->m_int_source[i] & 3);
+ LOGINT(" - Found an INT request to ack while reading RR2\n");
+ elem = Z80_DAISY_IEO; // Set IUS bit (called IEO in z80 daisy lingo)
+ m_uart->check_interrupts();
+ break;
+ }
+ i++;
+ }
+ // If no pending interrupt were found set variable bits to ones.
+ if (i >= 6)
+ {
+ m_rr2 |= 0x1F;
+ m_uart->m_chanA->m_rr0 &= ~z80sio_channel::RR0_INTERRUPT_PENDING;
+ }
+ }
+ return m_rr2;
}
//-------------------------------------------------
// control_read - read control register
//-------------------------------------------------
-
uint8_t z80sio_channel::control_read()
{
uint8_t data = 0;
@@ -916,6 +976,8 @@ uint8_t z80sio_channel::control_read()
LOG("%s %s unsupported register:%02x\n",FUNCNAME, tag(), reg);
}
+ LOGR(" * %s %c Reg %02x -> %02x - %s\n", tag(), 'A' + m_index, reg, data, std::array<char const *, 3>
+ {{"RR0 status register", "RR1 - Special Receive Conditions", "RR2 - Interrupt Vector"}}[reg]);
return data;
}
@@ -946,47 +1008,84 @@ void z80sio_channel::do_sioreg_wr0_resets(uint8_t data)
void z80sio_channel::do_sioreg_wr0(uint8_t data)
{
m_wr0 = data;
+
+ if ((data & WR0_COMMAND_MASK) != WR0_NULL)
+ LOGSETUP(" * %s %c Reg %02x <- %02x \n", m_owner->tag(), 'A' + m_index, 0, data);
switch (data & WR0_COMMAND_MASK)
{
case WR0_NULL:
- LOG("Z80SIO \"%s\" Channel %c : Null\n", m_owner->tag(), 'A' + m_index);
+ LOGCMD("%s %s Ch:%c : Null command\n", FUNCNAME, tag(), 'A' + m_index);
+ break;
+ case WR0_SEND_ABORT:
+ LOGCMD("%s %s Ch:%c : Send abort command - not implemented\n", FUNCNAME, tag(), 'A' + m_index);
break;
case WR0_RESET_EXT_STATUS:
// reset external/status interrupt
+
m_rr0 &= ~(RR0_DCD | RR0_SYNC_HUNT | RR0_CTS | RR0_BREAK_ABORT);
// release the latch
+
m_rx_rr0_latch = 0;
// update register to reflect wire values TODO: Check if this will fire new interrupts
if (!m_dcd) m_rr0 |= RR0_DCD;
if (m_sync) m_rr0 |= RR0_SYNC_HUNT;
if (m_cts) m_rr0 |= RR0_CTS;
- LOG("Z80SIO \"%s\" Channel %c : Reset External/Status Interrupt\n", m_owner->tag(), 'A' + m_index);
+ // Clear any pending External interrupt
+ m_uart->m_int_state[m_index == z80sio_device::CHANNEL_A ? 4 : 5] = 0;
+
+ LOGINT("%s %s Ch:%c : Reset External/Status Interrupt\n", FUNCNAME, tag(), 'A' + m_index);
break;
case WR0_CHANNEL_RESET:
// channel reset
- LOG("Z80SIO \"%s\" Channel %c : Channel Reset\n", m_owner->tag(), 'A' + m_index);
+ LOGCMD("%s %s Ch:%c : Channel Reset\n", FUNCNAME, tag(), 'A' + m_index);
device_reset();
break;
case WR0_ENABLE_INT_NEXT_RX:
// enable interrupt on next receive character
- LOG("Z80SIO \"%s\" Channel %c : Enable Interrupt on Next Received Character\n", m_owner->tag(), 'A' + m_index);
+ LOGINT("%s %s Ch:%c : Enable Interrupt on Next Received Character\n", FUNCNAME, tag(), 'A' + m_index);
m_rx_first = 1;
break;
case WR0_RESET_TX_INT:
// reset transmitter interrupt pending
- LOG("Z80SIO \"%s\" Channel %c : Reset Transmitter Interrupt Pending\n", m_owner->tag(), 'A' + m_index);
- logerror("Z80SIO \"%s\" Channel %c : unsupported command: Reset Transmitter Interrupt Pending\n", m_owner->tag(), 'A' + m_index);
+ {
+ uint8_t priority = 3; // Assume TxB
+ // Check if it is TxA
+ if (m_index == z80sio_device::CHANNEL_A)
+ {
+ // Check if priority bit is cleared
+ priority = (m_uart->m_chanA->m_wr2 & z80sio_channel::WR2_PRIORITY) == 0 ? 1 : 2;
+ }
+ m_uart->m_int_state[priority] = 0;
+ LOGINT("%s %s Ch:%c : Reset TX Interrupt, priority:%d\n", FUNCNAME, tag(), 'A' + m_index, priority);
+ }
+ m_uart->check_interrupts();
+ LOGCMD("%s %s Ch:%c : Reset Transmitter Interrupt Pending\n", FUNCNAME, tag(), 'A' + m_index);
break;
case WR0_ERROR_RESET:
// error reset
- LOG("Z80SIO \"%s\" Channel %c : Error Reset\n", m_owner->tag(), 'A' + m_index);
+ LOGCMD("%s %s Ch:%c : Error Reset\n", FUNCNAME, tag(), 'A' + m_index);
m_rr1 &= ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR);
break;
case WR0_RETURN_FROM_INT:
- // return from interrupt
- LOG("Z80SIO \"%s\" Channel %c : Return from Interrupt\n", m_owner->tag(), 'A' + m_index);
- m_uart->z80daisy_irq_reti();
+ LOGINT("%s %s Ch:%c : Return from interrupt\n", FUNCNAME, tag(), 'A' + m_index);
+ {
+ int found = 0;
+ // loop over all interrupt sources
+ for (auto & elem : m_uart->m_int_state)
+ {
+ // find the first channel with an interrupt requested
+ if (elem & (Z80_DAISY_IEO))
+ {
+ // clear the IEO state and update the IRQs
+ elem &= ~(Z80_DAISY_IEO);
+ m_uart->check_interrupts();
+ found = 1;
+ break;
+ }
+ }
+ LOGINT(" - %s\n", found == 0 ? "failed to find an interrupt to clear IEO on!" : "cleared IEO");
+ }
break;
default:
LOG("Z80SIO \"%s\" Channel %c : Unsupported WR0 command %02x mask %02x\n", m_owner->tag(), 'A' + m_index, data, WR0_REGISTER_MASK);
@@ -1029,14 +1128,6 @@ void z80sio_channel::do_sioreg_wr1(uint8_t data)
void z80sio_channel::do_sioreg_wr2(uint8_t data)
{
m_wr2 = data;
- if (m_index == z80sio_device::CHANNEL_B)
- {
- if (m_wr1 & z80sio_channel::WR1_STATUS_VECTOR)
- m_rr2 = ( m_rr2 & 0x0e ) | ( m_wr2 & 0xF1);
- else
- m_rr2 = m_wr2;
- }
- m_uart->check_interrupts();
LOG("Z80SIO \"%s\" Channel %c : Interrupt Vector %02x\n", m_owner->tag(), 'A' + m_index, data);
}
@@ -1088,7 +1179,8 @@ void z80sio_channel::control_write(uint8_t data)
if (reg != 0)
{
- LOGSETUP(" * %s %c Reg %02x <- %02x \n", m_owner->tag(), 'A' + m_index, reg, data);
+ LOGSETUP(" * %s %c Reg %02x <- %02x - %s\n", tag(), 'A' + m_index, reg, data, std::array<char const *, 8>
+ {{"WR0", "WR1", "WR2", "WR3 - Async Rx setup", "WR4 - Async Clock, Parity and stop bits", "WR5 - Async Tx setup", "WR6", "WR7"}}[reg]);
// mask out register index
m_wr0 &= ~WR0_REGISTER_MASK;
}
@@ -1118,18 +1210,15 @@ uint8_t z80sio_channel::data_read()
{
uint8_t data = 0;
- if (m_rx_fifo >= 0)
+ if (!m_rx_data_fifo.empty())
{
// load data from the FIFO
- data = m_rx_data_fifo[m_rx_fifo];
+ data = m_rx_data_fifo.dequeue();
// load error status from the FIFO
- m_rr1 = (m_rr1 & ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR)) | m_rx_error_fifo[m_rx_fifo];
+ m_rr1 = (m_rr1 & ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR)) | m_rx_error_fifo.dequeue();
- // decrease FIFO pointer
- m_rx_fifo--;
-
- if (m_rx_fifo < 0)
+ if (m_rx_data_fifo.empty())
{
// no more characters available in the FIFO
m_rr0 &= ~ RR0_RX_CHAR_AVAILABLE;
@@ -1151,7 +1240,7 @@ void z80sio_channel::data_write(uint8_t data)
if ((m_wr5 & WR5_TX_ENABLE) && is_transmit_register_empty())
{
- LOG("Z80SIO \"%s\" Channel %c : Transmit Data Byte '%02x'\n", m_owner->tag(), 'A' + m_index, m_tx_data);
+ LOGTX("Z80SIO \"%s\" Channel %c : Transmit Data Byte '%02x'\n", m_owner->tag(), 'A' + m_index, m_tx_data);
transmit_register_setup(m_tx_data);
@@ -1163,7 +1252,7 @@ void z80sio_channel::data_write(uint8_t data)
}
else
{
- LOG(" Transmitter %s, data byte dropped\n", m_wr5 & WR5_TX_ENABLE ? "not enabled" : "not emptied");
+ LOGTX(" Transmitter %s, data byte dropped\n", m_wr5 & WR5_TX_ENABLE ? "not enabled" : "not emptied");
m_rr0 &= ~RR0_TX_BUFFER_EMPTY;
}
@@ -1176,9 +1265,9 @@ void z80sio_channel::data_write(uint8_t data)
//-------------------------------------------------
void z80sio_channel::receive_data(uint8_t data)
{
- LOG("%s(%02x) %s:%c\n",FUNCNAME, data, tag(), 'A' + m_index);
+ LOGRCV("%s(%02x) %s:%c\n",FUNCNAME, data, tag(), 'A' + m_index);
- if (m_rx_fifo == 2)
+ if (m_rx_data_fifo.full())
{
LOG(" Overrun detected\n");
// receive overrun error detected
@@ -1201,13 +1290,11 @@ void z80sio_channel::receive_data(uint8_t data)
}
else
{
- m_rx_fifo++;
+ // store received character and error status into FIFO
+ m_rx_data_fifo.enqueue(data);
+ m_rx_error_fifo.enqueue(m_rx_error);
}
- // store received character and error status into FIFO
- m_rx_data_fifo[m_rx_fifo] = data;
- m_rx_error_fifo[m_rx_fifo] = m_rx_error;
-
m_rr0 |= RR0_RX_CHAR_AVAILABLE;
// receive interrupt
@@ -1235,7 +1322,6 @@ void z80sio_channel::receive_data(uint8_t data)
//-------------------------------------------------
// cts_w - clear to send handler
//-------------------------------------------------
-
WRITE_LINE_MEMBER( z80sio_channel::cts_w )
{
LOG("%s(%02x) %s:%c\n",FUNCNAME, state, tag(), 'A' + m_index);
@@ -1274,7 +1360,6 @@ WRITE_LINE_MEMBER( z80sio_channel::cts_w )
//-------------------------------------------------
// dcd_w - data carrier detected handler
//-------------------------------------------------
-
WRITE_LINE_MEMBER( z80sio_channel::dcd_w )
{
LOG("Z80SIO \"%s\" Channel %c : DCD %u\n", m_owner->tag(), 'A' + m_index, state);
@@ -1312,7 +1397,6 @@ WRITE_LINE_MEMBER( z80sio_channel::dcd_w )
//-------------------------------------------------
// sh_w - Sync Hunt handler
//-------------------------------------------------
-
WRITE_LINE_MEMBER( z80sio_channel::sync_w )
{
LOG("Z80SIO \"%s\" Channel %c : Sync %u\n", m_owner->tag(), 'A' + m_index, state);
@@ -1345,7 +1429,6 @@ WRITE_LINE_MEMBER( z80sio_channel::sync_w )
//-------------------------------------------------
// rxc_w - receive clock
//-------------------------------------------------
-
WRITE_LINE_MEMBER( z80sio_channel::rxc_w )
{
//LOG("Z80SIO \"%s\" Channel %c : Receiver Clock Pulse\n", m_owner->tag(), m_index + 'A');
@@ -1367,7 +1450,6 @@ WRITE_LINE_MEMBER( z80sio_channel::rxc_w )
//-------------------------------------------------
// txc_w - transmit clock
//-------------------------------------------------
-
WRITE_LINE_MEMBER( z80sio_channel::txc_w )
{
//LOG("Z80SIO \"%s\" Channel %c : Transmitter Clock Pulse\n", m_owner->tag(), m_index + 'A');
@@ -1430,7 +1512,6 @@ void z80sio_channel::update_serial()
//-------------------------------------------------
// set_dtr -
//-------------------------------------------------
-
void z80sio_channel::set_dtr(int state)
{
LOG("%s(%d)\n", FUNCNAME, state);
@@ -1445,7 +1526,6 @@ void z80sio_channel::set_dtr(int state)
//-------------------------------------------------
// write_rx -
//-------------------------------------------------
-
WRITE_LINE_MEMBER(z80sio_channel::write_rx)
{
m_rxd = state;