mirror of
https://github.com/holub/mame
synced 2025-05-31 10:01:51 +03:00
454 lines
10 KiB
C
454 lines
10 KiB
C
/***************************************************************************
|
|
|
|
Motorola MC2661/MC68661 Enhanced Programmable Communications Interface
|
|
|
|
Copyright the MESS Team.
|
|
Visit http://mamedev.org for licensing and usage restrictions.
|
|
|
|
***************************************************************************/
|
|
|
|
#include "mc2661.h"
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// DEVICE DEFINITIONS
|
|
//**************************************************************************
|
|
|
|
const device_type MC2661 = &device_creator<mc2661_device>;
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// MACROS / CONSTANTS
|
|
//**************************************************************************
|
|
|
|
#define LOG 0
|
|
|
|
|
|
enum
|
|
{
|
|
REGISTER_HOLDING = 0,
|
|
REGISTER_STATUS,
|
|
REGISTER_SYNC = REGISTER_STATUS,
|
|
REGISTER_MODE,
|
|
REGISTER_COMMAND
|
|
};
|
|
|
|
|
|
#define MODE_BAUD_RATE (m_mr[0] & 0x03)
|
|
#define MODE_CHARACTER ((m_mr[0] >> 2) & 0x03)
|
|
#define MODE_PARITY BIT(m_mr[0], 4)
|
|
#define MODE_PARITY_EVEN BIT(m_mr[0], 5)
|
|
#define MODE_TRANSPARENT BIT(m_mr[0], 6)
|
|
#define MODE_SINGLE_SYN BIT(m_mr[0], 7)
|
|
#define MODE_STOP_BITS ((m_mr[0] >> 6) & 0x03)
|
|
|
|
|
|
enum
|
|
{
|
|
STOP_BITS_INVALID = 0,
|
|
STOP_BITS_1,
|
|
STOP_BITS_1_5,
|
|
STOP_BITS_2
|
|
};
|
|
|
|
|
|
#define SYN1 m_sync[0]
|
|
#define SYN2 m_sync[1]
|
|
#define DLE m_sync[2]
|
|
|
|
|
|
#define COMMAND_TXEN BIT(m_cr, 0)
|
|
#define COMMAND_DTR BIT(m_cr, 1)
|
|
#define COMMAND_RXEN BIT(m_cr, 2)
|
|
#define COMMAND_BREAK BIT(m_cr, 3)
|
|
#define COMMAND_DLE BIT(m_cr, 3)
|
|
#define COMMAND_RESET BIT(m_cr, 4)
|
|
#define COMMAND_RTS BIT(m_cr, 5)
|
|
#define COMMAND_MODE (m_cr >> 6)
|
|
|
|
|
|
enum
|
|
{
|
|
MODE_NORMAL = 0,
|
|
MODE_ASYNC,
|
|
MODE_LOCAL_LOOP_BACK,
|
|
MODE_REMOTE_LOOP_BACK
|
|
};
|
|
|
|
|
|
#define STATUS_TXRDY 0x01
|
|
#define STATUS_RXRDY 0x02
|
|
#define STATUS_TXEMT 0x04
|
|
#define STATUS_PE 0x08
|
|
#define STATUS_DLE 0x08
|
|
#define STATUS_OVERRUN 0x10
|
|
#define STATUS_FE 0x20
|
|
#define STATUS_SYN 0x20
|
|
#define STATUS_DCD 0x40
|
|
#define STATUS_DSR 0x80
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// LIVE DEVICE
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// mc2661_device - constructor
|
|
//-------------------------------------------------
|
|
|
|
mc2661_device::mc2661_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
|
: device_t(mconfig, MC2661, "MC2661", tag, owner, clock, "mc2661", __FILE__),
|
|
device_serial_interface(mconfig, *this)
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// device_config_complete - perform any
|
|
// operations now that the configuration is
|
|
// complete
|
|
//-------------------------------------------------
|
|
|
|
void mc2661_device::device_config_complete()
|
|
{
|
|
// inherit a copy of the static data
|
|
const mc2661_interface *intf = reinterpret_cast<const mc2661_interface *>(static_config());
|
|
if (intf != NULL)
|
|
*static_cast<mc2661_interface *>(this) = *intf;
|
|
|
|
// or initialize to defaults if none provided
|
|
else
|
|
{
|
|
memset(&m_in_rxd_cb, 0, sizeof(m_in_rxd_cb));
|
|
memset(&m_out_txd_cb, 0, sizeof(m_out_txd_cb));
|
|
memset(&m_out_rxrdy_cb, 0, sizeof(m_out_rxrdy_cb));
|
|
memset(&m_out_txrdy_cb, 0, sizeof(m_out_txrdy_cb));
|
|
memset(&m_out_rts_cb, 0, sizeof(m_out_rts_cb));
|
|
memset(&m_out_dtr_cb, 0, sizeof(m_out_dtr_cb));
|
|
memset(&m_out_txemt_dschg_cb, 0, sizeof(m_out_txemt_dschg_cb));
|
|
memset(&m_out_bkdet_cb, 0, sizeof(m_out_bkdet_cb));
|
|
memset(&m_out_xsync_cb, 0, sizeof(m_out_xsync_cb));
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// device_start - device-specific startup
|
|
//-------------------------------------------------
|
|
|
|
void mc2661_device::device_start()
|
|
{
|
|
// resolve callbacks
|
|
m_in_rxd_func.resolve(m_in_rxd_cb, *this);
|
|
m_out_txd_func.resolve(m_out_txd_cb, *this);
|
|
m_out_rxrdy_func.resolve(m_out_rxrdy_cb, *this);
|
|
m_out_txrdy_func.resolve(m_out_txrdy_cb, *this);
|
|
m_out_rts_func.resolve(m_out_rts_cb, *this);
|
|
m_out_dtr_func.resolve(m_out_dtr_cb, *this);
|
|
m_out_txemt_dschg_func.resolve(m_out_txemt_dschg_cb, *this);
|
|
m_out_bkdet_func.resolve(m_out_bkdet_cb, *this);
|
|
m_out_xsync_func.resolve(m_out_xsync_cb, *this);
|
|
|
|
// create the timers
|
|
if (m_rxc > 0)
|
|
{
|
|
set_rcv_rate(m_rxc);
|
|
}
|
|
|
|
if (m_txc > 0)
|
|
{
|
|
set_tra_rate(m_txc);
|
|
}
|
|
|
|
// save state
|
|
save_item(NAME(m_rhr));
|
|
save_item(NAME(m_thr));
|
|
save_item(NAME(m_cr));
|
|
save_item(NAME(m_sr));
|
|
save_item(NAME(m_mr));
|
|
save_item(NAME(m_sync));
|
|
save_item(NAME(m_mode_index));
|
|
save_item(NAME(m_sync_index));
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// device_reset - device-specific reset
|
|
//-------------------------------------------------
|
|
|
|
void mc2661_device::device_reset()
|
|
{
|
|
receive_register_reset();
|
|
transmit_register_reset();
|
|
|
|
m_mr[0] = m_mr[1] = 0;
|
|
m_sync[0] = m_sync[1] = m_sync[2] = 0;
|
|
m_cr = 0;
|
|
m_sr = 0;
|
|
|
|
m_mode_index = 0;
|
|
m_sync_index = 0;
|
|
|
|
m_out_txd_func(1);
|
|
m_out_rxrdy_func(CLEAR_LINE);
|
|
m_out_txrdy_func(CLEAR_LINE);
|
|
m_out_rts_func(1);
|
|
m_out_dtr_func(1);
|
|
m_out_txemt_dschg_func(CLEAR_LINE);
|
|
m_out_bkdet_func(0);
|
|
m_out_xsync_func(0);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// tra_callback -
|
|
//-------------------------------------------------
|
|
|
|
void mc2661_device::tra_callback()
|
|
{
|
|
if (m_out_txd_func.isnull())
|
|
transmit_register_send_bit();
|
|
else
|
|
m_out_txd_func(transmit_register_get_data_bit());
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// tra_complete -
|
|
//-------------------------------------------------
|
|
|
|
void mc2661_device::tra_complete()
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// rcv_callback -
|
|
//-------------------------------------------------
|
|
|
|
void mc2661_device::rcv_callback()
|
|
{
|
|
if (m_in_rxd_func.isnull())
|
|
receive_register_update_bit(get_in_data_bit());
|
|
else
|
|
receive_register_update_bit(m_in_rxd_func());
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// rcv_complete -
|
|
//-------------------------------------------------
|
|
|
|
void mc2661_device::rcv_complete()
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// input_callback -
|
|
//-------------------------------------------------
|
|
|
|
void mc2661_device::input_callback(UINT8 state)
|
|
{
|
|
m_input_state = state;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// read - register read
|
|
//-------------------------------------------------
|
|
|
|
READ8_MEMBER( mc2661_device::read )
|
|
{
|
|
UINT8 data = 0;
|
|
|
|
switch (offset & 0x03)
|
|
{
|
|
case REGISTER_HOLDING:
|
|
data = m_rhr;
|
|
break;
|
|
|
|
case REGISTER_STATUS:
|
|
data = m_sr;
|
|
break;
|
|
|
|
case REGISTER_MODE:
|
|
data = m_mr[m_mode_index];
|
|
|
|
m_mode_index++;
|
|
m_mode_index &= 0x01;
|
|
break;
|
|
|
|
case REGISTER_COMMAND:
|
|
m_mode_index = 0;
|
|
m_sync_index = 0;
|
|
|
|
data = m_cr;
|
|
break;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// write - register write
|
|
//-------------------------------------------------
|
|
|
|
WRITE8_MEMBER( mc2661_device::write )
|
|
{
|
|
switch (offset & 0x03)
|
|
{
|
|
case REGISTER_HOLDING:
|
|
if (LOG) logerror("MC2661 '%s' Transmit Holding Register: %02x\n", tag(), data);
|
|
|
|
m_thr = data;
|
|
break;
|
|
|
|
case REGISTER_SYNC:
|
|
if (LOG) logerror("MC2661 '%s' Sync Register %u: %02x\n", tag(), m_sync_index + 1, data);
|
|
|
|
m_sync[m_sync_index] = data;
|
|
|
|
m_sync_index++;
|
|
if (m_sync_index == 3) m_sync_index = 0;
|
|
break;
|
|
|
|
case REGISTER_MODE:
|
|
if (LOG) logerror("MC2661 '%s' Mode Register %u: %02x\n", tag(), m_mode_index + 1, data);
|
|
|
|
m_mr[m_mode_index] = data;
|
|
|
|
if (m_mode_index == 0)
|
|
{
|
|
int word_length = 5 + MODE_CHARACTER;
|
|
float stop_bits = 0;
|
|
int parity_code;
|
|
|
|
switch (MODE_STOP_BITS)
|
|
{
|
|
case STOP_BITS_1: stop_bits = 1; break;
|
|
case STOP_BITS_1_5: stop_bits = 1.5; break;
|
|
case STOP_BITS_2: stop_bits = 2; break;
|
|
}
|
|
|
|
if (!MODE_PARITY) parity_code = SERIAL_PARITY_NONE;
|
|
else if (MODE_PARITY_EVEN) parity_code = SERIAL_PARITY_EVEN;
|
|
else parity_code = SERIAL_PARITY_ODD;
|
|
|
|
set_data_frame(word_length, stop_bits, parity_code);
|
|
}
|
|
|
|
m_mode_index++;
|
|
m_mode_index &= 0x01;
|
|
break;
|
|
|
|
case REGISTER_COMMAND:
|
|
if (LOG) logerror("MC2661 '%s' Command Register: %02x\n", tag(), data);
|
|
|
|
m_cr = data & 0xef;
|
|
|
|
if (COMMAND_RESET)
|
|
{
|
|
m_sr &= ~(STATUS_FE | STATUS_OVERRUN | STATUS_PE);
|
|
}
|
|
|
|
m_out_dtr_func(!COMMAND_DTR);
|
|
m_out_rts_func(!COMMAND_RTS);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// rxc_w - receiver clock
|
|
//-------------------------------------------------
|
|
|
|
WRITE_LINE_MEMBER( mc2661_device::rxc_w )
|
|
{
|
|
rcv_clock();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// txc_w - transmitter clock
|
|
//-------------------------------------------------
|
|
|
|
WRITE_LINE_MEMBER( mc2661_device::txc_w )
|
|
{
|
|
tra_clock();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// dsr_w - data set ready
|
|
//-------------------------------------------------
|
|
|
|
WRITE_LINE_MEMBER( mc2661_device::dsr_w )
|
|
{
|
|
if (LOG) logerror("MC2661 '%s' Data Set Ready: %u\n", tag(), state);
|
|
|
|
if (state)
|
|
{
|
|
m_sr &= ~STATUS_DSR;
|
|
}
|
|
else
|
|
{
|
|
m_sr |= STATUS_DSR;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// dcd_w - data carrier detect
|
|
//-------------------------------------------------
|
|
|
|
WRITE_LINE_MEMBER( mc2661_device::dcd_w )
|
|
{
|
|
if (LOG) logerror("MC2661 '%s' Data Carrier Detect: %u\n", tag(), state);
|
|
|
|
if (state)
|
|
{
|
|
m_sr &= ~STATUS_DCD;
|
|
}
|
|
else
|
|
{
|
|
m_sr |= STATUS_DCD;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// cts_w - clear to send
|
|
//-------------------------------------------------
|
|
|
|
WRITE_LINE_MEMBER( mc2661_device::cts_w )
|
|
{
|
|
if (LOG) logerror("MC2661 '%s' Clear to Send: %u\n", tag(), state);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// rxrdy_r - receiver ready
|
|
//-------------------------------------------------
|
|
|
|
READ_LINE_MEMBER( mc2661_device::rxrdy_r )
|
|
{
|
|
return (m_sr & STATUS_RXRDY) ? ASSERT_LINE : CLEAR_LINE;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// txemt_r - transmitter empty
|
|
//-------------------------------------------------
|
|
|
|
READ_LINE_MEMBER( mc2661_device::txemt_r )
|
|
{
|
|
return (m_sr & STATUS_TXEMT) ? ASSERT_LINE : CLEAR_LINE;
|
|
}
|