Add Exar XR68C681 support to mc68681.cpp (again) (#3755)
* skeleton support for Exar XR68C681 dual UART device * added support for XR68C681 MISR register * XR68C681 extended baud rate support Well, at least in theory. Here goes nothing! * copyright update * Style changes as described in mamedev pull request #3755 * More style changes for PR #3755 in mamedev hex literals should be lower case also some spacing corrected
This commit is contained in:
parent
47e045d545
commit
e6938b4226
@ -1,16 +1,18 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Mariusz Wojcieszek, R. Belmont
|
||||
// copyright-holders:Mariusz Wojcieszek, R. Belmont, Joseph Zatarski
|
||||
/*
|
||||
2681 DUART
|
||||
68681 DUART
|
||||
28C94 QUART
|
||||
68340 serial module
|
||||
XR68C681 DUART (backwards compatible with 68681 with some improvements)
|
||||
|
||||
Written by Mariusz Wojcieszek
|
||||
Updated by Jonathan Gevaryahu AKA Lord Nightmare
|
||||
Improved interrupt handling by R. Belmont
|
||||
Rewrite and modernization in progress by R. Belmont
|
||||
Addition of the duart compatible 68340 serial module support by Edstrom
|
||||
Support for the Exar XR68C681 by Joseph Zatarski (July of 2018)
|
||||
|
||||
The main incompatibility between the 2681 and 68681 (Signetics and Motorola each
|
||||
manufactured both versions of the chip) is that the 68681 has a R/W input and
|
||||
@ -21,6 +23,26 @@
|
||||
The command register addresses should never be read from. Doing so may place
|
||||
the baud rate generator into a test mode which drives the parallel outputs with
|
||||
internal counters and causes serial ports to operate at uncontrollable rates.
|
||||
|
||||
Exar XR68C681
|
||||
|
||||
The XR68C681 is an improvement upon the MC68681 which supports more baud
|
||||
rates, adds an additional MISR (masked ISR) register, and adds a low-power
|
||||
standby mode. There may be other differences, but these are the most
|
||||
notable.
|
||||
|
||||
The extra baud rates are implemented by an 'X' bit for each channel. The X
|
||||
bit chooses between two baud rate tables, in addition to the ACR[7] bit.
|
||||
The X bit is changed by additional commands that are written to CRA and
|
||||
CRB.
|
||||
|
||||
The MISR is a read only register that takes the place of the 'BRG Test'
|
||||
register on the MC68681.
|
||||
|
||||
The low power standby mode is entered and left by a command written to CRA
|
||||
or CRB registers. Writing the commands to either register affects the whole
|
||||
DUART, not just one channel. Resetting the DUART also leaves low power
|
||||
mode.
|
||||
*/
|
||||
|
||||
#include "emu.h"
|
||||
@ -41,8 +63,10 @@ static const char *const duart68681_reg_write_names[0x10] =
|
||||
"MRA", "CSRA", "CRA", "THRA", "ACR", "IMR", "CRUR", "CTLR", "MRB", "CSRB", "CRB", "THRB", "IVR", "OPCR", "Set OP Bits", "Reset OP Bits"
|
||||
};
|
||||
|
||||
static const int baud_rate_ACR_0[] = { 50, 110, 134, 200, 300, 600, 1200, 1050, 2400, 4800, 7200, 9600, 38400, 0, 0, 0 };
|
||||
static const int baud_rate_ACR_1[] = { 75, 110, 134, 150, 300, 600, 1200, 2000, 2400, 4800, 1800, 9600, 19200, 0, 0, 0 };
|
||||
static const int baud_rate_ACR_0[] = { 50, 110, 134, 200, 300, 600, 1200, 1050, 2400, 4800, 7200, 9600, 38400, 0, 0, 0 }; /* xr68c681 X=0 */
|
||||
static const int baud_rate_ACR_0_X_1[] = { 75, 110, 134, 150, 3600, 14400, 28800, 57600, 115200, 4800, 1800, 9600, 19200, 0, 0, 0 };
|
||||
static const int baud_rate_ACR_1[] = { 75, 110, 134, 150, 300, 600, 1200, 2000, 2400, 4800, 1800, 9600, 19200, 0, 0, 0 }; /* xr68c681 X=0 */
|
||||
static const int baud_rate_ACR_1_X_1[] = { 50, 110, 134, 200, 3600, 14400, 28800, 57600, 115200, 4800, 7200, 9600, 38400, 0, 0, 0 };
|
||||
|
||||
#define INT_INPUT_PORT_CHANGE 0x80
|
||||
#define INT_DELTA_BREAK_B 0x40
|
||||
@ -74,6 +98,7 @@ DEFINE_DEVICE_TYPE(SCN2681, scn2681_device, "scn2681", "SCN2681 DUART")
|
||||
DEFINE_DEVICE_TYPE(MC68681, mc68681_device, "mc68681", "MC68681 DUART")
|
||||
DEFINE_DEVICE_TYPE(SC28C94, sc28c94_device, "sc28c94", "SC28C94 QUART")
|
||||
DEFINE_DEVICE_TYPE(MC68340_DUART, mc68340_duart_device, "mc68340duart", "MC68340 DUART Device")
|
||||
DEFINE_DEVICE_TYPE(XR68C681, xr68c681_device, "xr68c681", "XR68C681 DUART")
|
||||
DEFINE_DEVICE_TYPE(DUART_CHANNEL, duart_channel, "duart_channel", "DUART channel")
|
||||
|
||||
|
||||
@ -108,12 +133,17 @@ scn2681_device::scn2681_device(const machine_config &mconfig, const char *tag, d
|
||||
{
|
||||
}
|
||||
|
||||
mc68681_device::mc68681_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: duart_base_device(mconfig, MC68681, tag, owner, clock),
|
||||
mc68681_device::mc68681_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
|
||||
: duart_base_device(mconfig, type, tag, owner, clock),
|
||||
m_read_vector(false)
|
||||
{
|
||||
}
|
||||
|
||||
mc68681_device::mc68681_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: mc68681_device(mconfig, MC68681, tag, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
sc28c94_device::sc28c94_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: duart_base_device(mconfig, SC28C94, tag, owner, clock)
|
||||
{
|
||||
@ -136,6 +166,15 @@ mc68340_duart_device::mc68340_duart_device(const machine_config &mconfig, const
|
||||
{
|
||||
}
|
||||
|
||||
xr68c681_device::xr68c681_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: mc68681_device(mconfig, XR68C681, tag, owner, clock),
|
||||
m_XTXA(false),
|
||||
m_XRXA(false),
|
||||
m_XTXB(false),
|
||||
m_XRXB(false)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// set_clocks - configuration helper to set
|
||||
// the external clocks
|
||||
@ -182,6 +221,16 @@ void mc68681_device::device_start()
|
||||
save_item(NAME(IVR));
|
||||
}
|
||||
|
||||
void xr68c681_device::device_start()
|
||||
{
|
||||
mc68681_device::device_start();
|
||||
|
||||
save_item(NAME(m_XTXA));
|
||||
save_item(NAME(m_XRXA));
|
||||
save_item(NAME(m_XTXB));
|
||||
save_item(NAME(m_XRXB));
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
device reset callback
|
||||
-------------------------------------------------*/
|
||||
@ -209,6 +258,13 @@ void mc68681_device::device_reset()
|
||||
m_read_vector = false;
|
||||
}
|
||||
|
||||
void xr68c681_device::device_reset()
|
||||
{
|
||||
mc68681_device::device_reset();
|
||||
|
||||
m_XTXA = m_XRXA = m_XTXB = m_XRXB = false;
|
||||
}
|
||||
|
||||
MACHINE_CONFIG_START(duart_base_device::device_add_mconfig)
|
||||
MCFG_DEVICE_ADD(CHANA_TAG, DUART_CHANNEL, 0)
|
||||
MCFG_DEVICE_ADD(CHANB_TAG, DUART_CHANNEL, 0)
|
||||
@ -474,6 +530,17 @@ READ8_MEMBER( sc28c94_device::read )
|
||||
return r;
|
||||
}
|
||||
|
||||
READ8_MEMBER(xr68c681_device::read)
|
||||
{
|
||||
if (offset == 0x02)
|
||||
{
|
||||
LOG("Reading XR68C681 (%s) reg 0x02 (MISR)\n", tag());
|
||||
return ISR & IMR;
|
||||
}
|
||||
else
|
||||
return mc68681_device::read(space, offset, mem_mask);
|
||||
}
|
||||
|
||||
READ8_MEMBER(duart_base_device::read)
|
||||
{
|
||||
uint8_t r = 0xff;
|
||||
@ -624,6 +691,81 @@ WRITE8_MEMBER( sc28c94_device::write )
|
||||
}
|
||||
}
|
||||
|
||||
WRITE8_MEMBER(xr68c681_device::write)
|
||||
{
|
||||
if (offset == 0x02) /* CRA */
|
||||
switch (data >> 4)
|
||||
{
|
||||
case 0x08: /* set RX extend bit */
|
||||
m_XRXA = true;
|
||||
m_chanA->baud_updated();
|
||||
data &= 0x0f; /* disable command before we send it off to 68681 */
|
||||
break;
|
||||
|
||||
case 0x09: /* clear RX extend bit */
|
||||
m_XRXA = false;
|
||||
m_chanA->baud_updated();
|
||||
data &= 0x0f;
|
||||
break;
|
||||
|
||||
case 0x0a: /* set TX extend bit */
|
||||
m_XTXA = true;
|
||||
m_chanA->baud_updated();
|
||||
data &= 0x0f;
|
||||
break;
|
||||
|
||||
case 0x0b: /* clear TX extend bit */
|
||||
m_XTXA = false;
|
||||
m_chanA->baud_updated();
|
||||
data &= 0x0f;
|
||||
break;
|
||||
|
||||
case 0x0c: /* enter low power mode TODO: unimplemented */
|
||||
case 0x0d: /* leave low power mode */
|
||||
case 0x0e: /* reserved */
|
||||
case 0x0f: /* reserved */
|
||||
data &= 0x0f;
|
||||
break;
|
||||
}
|
||||
|
||||
else if (offset == 0x0a) /* CRB */
|
||||
switch (data >> 4)
|
||||
{
|
||||
case 0x08: /* set RX extend bit */
|
||||
m_XRXB = true;
|
||||
m_chanB->baud_updated();
|
||||
data &= 0x0f;
|
||||
break;
|
||||
|
||||
case 0x09: /* clear RX extend bit */
|
||||
m_XRXB = false;
|
||||
m_chanB->baud_updated();
|
||||
data &= 0x0f;
|
||||
break;
|
||||
|
||||
case 0x0a: /* set TX extend bit */
|
||||
m_XTXB = true;
|
||||
m_chanB->baud_updated();
|
||||
data &= 0x0f;
|
||||
break;
|
||||
|
||||
case 0x0b: /* clear TX extend bit */
|
||||
m_XTXB = false;
|
||||
m_chanB->baud_updated();
|
||||
data &= 0x0f;
|
||||
break;
|
||||
|
||||
case 0x0c: /* enter low power mode TODO: unimplemented */
|
||||
case 0x0d: /* leave low power mode */
|
||||
case 0x0e: /* reserved */
|
||||
case 0x0f: /* reserved */
|
||||
data &= 0x0f;
|
||||
break;
|
||||
}
|
||||
|
||||
mc68681_device::write(space, offset, data, mem_mask); /* pass on 68681 command */
|
||||
}
|
||||
|
||||
WRITE8_MEMBER(duart_base_device::write)
|
||||
{
|
||||
offset &= 0x0f;
|
||||
@ -665,8 +807,8 @@ WRITE8_MEMBER( duart_base_device::write )
|
||||
if ((((IPCR>>4) & data) & 0x0f) != 0)
|
||||
set_ISR_bits(INT_INPUT_PORT_CHANGE);
|
||||
|
||||
m_chanA->ACR_updated();
|
||||
m_chanB->ACR_updated();
|
||||
m_chanA->baud_updated();
|
||||
m_chanB->baud_updated();
|
||||
m_chanA->update_interrupts();
|
||||
m_chanB->update_interrupts();
|
||||
break;
|
||||
@ -808,7 +950,7 @@ duart_channel *duart_base_device::get_channel(int chan)
|
||||
return m_chanB;
|
||||
}
|
||||
|
||||
int duart_base_device::calc_baud(int ch, uint8_t data)
|
||||
int duart_base_device::calc_baud(int ch, bool rx, uint8_t data)
|
||||
{
|
||||
int baud_rate;
|
||||
|
||||
@ -853,6 +995,45 @@ int duart_base_device::calc_baud(int ch, uint8_t data)
|
||||
return baud_rate;
|
||||
}
|
||||
|
||||
int xr68c681_device::calc_baud(int ch, bool rx, uint8_t data)
|
||||
{
|
||||
int baud_rate;
|
||||
|
||||
|
||||
baud_rate = baud_rate_ACR_0[data & 0x0f];
|
||||
|
||||
if (ch == 0)
|
||||
{
|
||||
if ((data & 0x0f) == 0x0e)
|
||||
baud_rate = ip3clk/16;
|
||||
else if ((data & 0x0f) == 0x0f)
|
||||
baud_rate = ip3clk;
|
||||
else if ((rx && m_XRXA) || (!rx && m_XTXA)) /* X = 1 */
|
||||
baud_rate = BIT(ACR, 7) == 0 ? baud_rate_ACR_0_X_1[data & 0x0f] : baud_rate_ACR_1_X_1[data & 0x0f];
|
||||
else /* X = 0 */
|
||||
baud_rate = BIT(ACR, 7) == 0 ? baud_rate_ACR_0[data & 0x0f] : baud_rate_ACR_1[data & 0x0f];
|
||||
}
|
||||
else if (ch == 1)
|
||||
{
|
||||
if ((data & 0x0f) == 0x0e)
|
||||
baud_rate = ip5clk/16;
|
||||
else if ((data & 0x0f) == 0x0f)
|
||||
baud_rate = ip5clk;
|
||||
else if ((rx && m_XRXB) || (!rx && m_XTXB)) /* X = 1 */
|
||||
baud_rate = BIT(ACR, 7) == 0 ? baud_rate_ACR_0_X_1[data & 0x0f] : baud_rate_ACR_1_X_1[data & 0x0f];
|
||||
else /* X = 0 */
|
||||
baud_rate = BIT(ACR, 7) == 0 ? baud_rate_ACR_0[data & 0x0f] : baud_rate_ACR_1[data & 0x0f];
|
||||
}
|
||||
|
||||
if ((baud_rate == 0) && ((data & 0xf) != 0xd))
|
||||
{
|
||||
LOG("Unsupported transmitter clock: channel %d, clock select = %02x\n", ch, data);
|
||||
}
|
||||
|
||||
//printf("%s ch %d setting baud to %d\n", tag(), ch, baud_rate);
|
||||
return baud_rate;
|
||||
}
|
||||
|
||||
void duart_base_device::clear_ISR_bits(int mask)
|
||||
{
|
||||
if ((ISR & mask) != 0)
|
||||
@ -958,7 +1139,7 @@ void duart_channel::tra_complete()
|
||||
m_uart->clear_ISR_bits(INT_TXRDYB);
|
||||
|
||||
// if local loopback is on, write the transmitted data as if a byte had been received
|
||||
if ((MR2 & 0xC0) == 0x80)
|
||||
if ((MR2 & 0xc0) == 0x80)
|
||||
{
|
||||
if (rx_fifo_num >= MC68681_RX_FIFO_SIZE)
|
||||
{
|
||||
@ -982,7 +1163,7 @@ void duart_channel::tra_complete()
|
||||
void duart_channel::tra_callback()
|
||||
{
|
||||
// don't actually send in loopback mode
|
||||
if ((MR2&0xC0) != 0x80)
|
||||
if ((MR2 & 0xc0) != 0x80)
|
||||
{
|
||||
int bit = transmit_register_get_data_bit();
|
||||
//printf("%s ch %d transmit %d\n", tag(), m_ch, bit);
|
||||
@ -1032,7 +1213,7 @@ void duart_channel::update_interrupts()
|
||||
}
|
||||
|
||||
// Handle the TxEMT and TxRDY bits based on mode
|
||||
switch(MR2&0xC0) // what mode are we in?
|
||||
switch (MR2 & 0xc0) // what mode are we in?
|
||||
{
|
||||
case 0x00: // normal mode
|
||||
if (tx_enabled)
|
||||
@ -1058,12 +1239,13 @@ void duart_channel::update_interrupts()
|
||||
SR &= ~STATUS_TRANSMITTER_EMPTY;
|
||||
}
|
||||
break;
|
||||
case 0xC0: // remote loopback mode
|
||||
case 0xc0: // remote loopback mode
|
||||
// write me, what the txrdy/txemt regs do for remote loopback mode is undocumented afaik, for now just clear both
|
||||
SR &= ~STATUS_TRANSMITTER_EMPTY;
|
||||
SR &= ~STATUS_TRANSMITTER_READY;
|
||||
break;
|
||||
}
|
||||
|
||||
// now handle the ISR bits
|
||||
if (SR & STATUS_TRANSMITTER_READY)
|
||||
{
|
||||
@ -1176,8 +1358,8 @@ void duart_channel::write_chan_reg(int reg, uint8_t data)
|
||||
|
||||
case 0x01: /* CSR */
|
||||
CSR = data;
|
||||
tx_baud_rate = m_uart->calc_baud(m_ch, data & 0xf);
|
||||
rx_baud_rate = m_uart->calc_baud(m_ch, (data>>4) & 0xf);
|
||||
tx_baud_rate = m_uart->calc_baud(m_ch, false, data & 0xf);
|
||||
rx_baud_rate = m_uart->calc_baud(m_ch, true, (data>>4) & 0xf);
|
||||
//printf("%s ch %d CSR %02x Tx baud %d Rx baud %d\n", tag(), m_ch, data, tx_baud_rate, rx_baud_rate);
|
||||
set_rcv_rate(rx_baud_rate);
|
||||
set_tra_rate(tx_baud_rate);
|
||||
@ -1371,7 +1553,7 @@ void duart_channel::write_TX(uint8_t data)
|
||||
update_interrupts();
|
||||
}
|
||||
|
||||
void duart_channel::ACR_updated()
|
||||
void duart_channel::baud_updated()
|
||||
{
|
||||
write_chan_reg(1, CSR);
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ public:
|
||||
|
||||
uint8_t read_rx_fifo();
|
||||
|
||||
void ACR_updated();
|
||||
void baud_updated();
|
||||
|
||||
uint8_t get_chan_CSR();
|
||||
|
||||
@ -167,10 +167,13 @@ protected:
|
||||
private:
|
||||
TIMER_CALLBACK_MEMBER(duart_timer_callback);
|
||||
|
||||
protected:
|
||||
/* registers */
|
||||
uint8_t ACR; /* Auxiliary Control Register */
|
||||
uint8_t IMR; /* Interrupt Mask Register */
|
||||
uint8_t ISR; /* Interrupt Status Register */
|
||||
|
||||
private:
|
||||
uint8_t OPCR; /* Output Port Conf. Register */
|
||||
uint8_t OPR; /* Output Port Register */
|
||||
PAIR CTR; /* Counter/Timer Preset Value */
|
||||
@ -186,7 +189,7 @@ private:
|
||||
double get_ct_rate();
|
||||
uint16_t get_ct_count();
|
||||
void start_ct(int count);
|
||||
int calc_baud(int ch, uint8_t data);
|
||||
virtual int calc_baud(int ch, bool rx, uint8_t data);
|
||||
void clear_ISR_bits(int mask);
|
||||
void set_ISR_bits(int mask);
|
||||
|
||||
@ -230,6 +233,7 @@ protected:
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
virtual void update_interrupts() override;
|
||||
mc68681_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
private:
|
||||
bool m_read_vector; // if this is read and IRQ is active, it counts as pulling IACK
|
||||
@ -270,10 +274,29 @@ protected:
|
||||
mc68340_duart_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
|
||||
};
|
||||
|
||||
class xr68c681_device : public mc68681_device
|
||||
{
|
||||
public:
|
||||
xr68c681_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
virtual DECLARE_READ8_MEMBER(read) override;
|
||||
virtual DECLARE_WRITE8_MEMBER(write) override;
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
private:
|
||||
virtual int calc_baud(int ch, bool rx, uint8_t data) override;
|
||||
|
||||
bool m_XTXA,m_XRXA,m_XTXB,m_XRXB; /* X bits for the BRG (selects between 2 BRG tables) */
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(SCN2681, scn2681_device)
|
||||
DECLARE_DEVICE_TYPE(MC68681, mc68681_device)
|
||||
DECLARE_DEVICE_TYPE(SC28C94, sc28c94_device)
|
||||
DECLARE_DEVICE_TYPE(MC68340_DUART, mc68340_duart_device)
|
||||
DECLARE_DEVICE_TYPE(XR68C681, xr68c681_device)
|
||||
DECLARE_DEVICE_TYPE(DUART_CHANNEL, duart_channel)
|
||||
|
||||
#endif // MAME_MACHINE_MC68681_H
|
||||
|
Loading…
Reference in New Issue
Block a user