summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/68561mpcc.cpp
blob: e44c53e3e8898ae2a2d95db50160470b5e730626 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                                                            
 





                                                           



                                                         
























                                                                                                                                   



                      


                                                                            





























                                                           
 






                                    
 
















                                                                                                                                                                                                          
                            


































                               
 

                                       

 





































                                                                                                         
 


                                       
 

                                                                                                                    
 

                                                                                                                        
 

                                                                                                                    
 

                                                                                                                        
 



                                                   
 


































                                                 

                                                                             

 



                                                   
 

                                             




                                  

                       
                       


                       
                       


                       
                       




                       

                       

                       
 



                               

                                                     





                                  

 


                               




                                                                                          





































































                                                                                                                                       









































































































































                                                                                                 
                                                                                            







                                                                                           
                                            




                                
                                           

                                           
                                                        






                                         
                                            







                                           
                                                   

                                                   
                                                                



                                                 
                                                    




                                        



                                                                      
 

                                                      
         
                                                           
                                                 
                                
         


                                                                            
         

                                 
         





                                                          
                                                            
 




                                                       
         










                                                                                      
                                                       
                                            
                 





                                                                                                       
                                                   
                 
 


                                                     



                                                       
         

                                                                            


            

                                


         



                                                                      
 




                                                   

 


                                                   


                                
 

                                   
                                                                            
 
                               



















                                                                   

 




                                                        
 





                                                     

 
 






                                                   
 

                                           
 

                                          
         
                              
         

                          
                                                                                      
                                                                                 
                                   
         


                                                                                            




                                                          



                                                                                           




                                                         
                 
         

 


                                                   
 
                                    
 


                                           
         
                         
         
 

                                           

 



                                                                          
 























                                                        

 























































                                                                                                                       



                                                   
 
                         


                      

                                          
                                          
                                            
                                           
                                          



                                                                         
                                           
                                           
                                                                                        


                                           

                                                                                    



                                            
                                                                                                    
         
                                                                
                    

 



                                                   
 
                                                                    

                      


                                                                        
                                         
                                        
                                       



                                                                           
                                        
                                        
                                                                                        


                                        

                                                                                    



                                         
                                                                                                    


         

                                                        



                                            
                                  


















                                                                                             

                        








                                            







                                                






                                                                                                          










                                                                                       












                                                      
 
                                       
 



















                                                                                                                 







                                                                 
                                             


                                                                    
                                                       
                 
         









                                                                                                                                       



                                                               





                                                           
 
 













                                               





                                                                                                   


                                                                                               
                                                                                                   



                                                                                                   







































                                                                                                                 





                                                           
                                  












                                                                                                                      


                                            







                                                                                       
                      









                                          




































                                                                                                           

                        


















                                                                                                                                                    

                        
















                                                           

                        













                                                           

                        


















                                                                                                                        

                        



















                                                                                                                      

                        







                                            
// license:BSD-3-Clause copyright-holders: Joakim Larsson Edstrom
/***************************************************************************

    MPCC Multi-Protocol Communications Controller emulation

    The MPCC was introduced in the late 80:ies by Rockwell

    The variants in the MPCC family are as follows:

    - 68560  with an 8 bit data bus
    - 68560A with an 8 bit data bus and some enhancements
    - 68561  with a 16 bit data bus
    - 68561A with a 16 bit data bus and some enhancements

FEATURES
------------------------------------------------------------------
 *   Full duplex synchronous/asynchronous receiver and transmitter
 *   Implements IBM Binary Synchronous Communications (BSC) in two coding formats: ASCII and EBCDIC
 *   Supports other synchronous character -oriented protocols (COP), such as six -bit BSC, X3.28k. ISO IS1745, ECMA-16, etc.
 *   Supports synchronous bit oriented protocols (BOP), such as SDLC, HDLC, X.25, etc.
 *   Asynchronous and isochronous modes
 *   Modem handshake interface
 *   High speed serial data rate (DC to 4 MHz)
 *   Internal oscillator and baud rate generator with programmable data rate
 *   Crystal or TTL level clock input and buffered clock output (8 MHz)
 *   Direct interface to 68008/68000 asynchronous bus
 *   Eight -character receiver and transmitter buffer registers
 *   22 directly addressable registers for flexible option selection, complete status reporting, and data transfer
 *   Three separate programmable interrupt vector numbers for receiver, transmitter and serial interface
 *   Maskable interrupt conditions for receiver, transmitter and serial interface
 *   Programmable microprocessor bus data transfer; polled, interrupt and two -channel DMA transfer compatible with MC68440/MC68450
 *   Clock control register for receiver clock divisor and receiver and transmitter clock routing
 *   Selectable full/half duplex, autoecho and local loop -back modes
 *   Selectable parity (enable, odd, even) and CRC (control field enable, CRC -16, CCITT V.41, VRC/LRC)
 *-------------------------------------------------------------------------------------------
 *       x = Features that has been implemented  p = partly n = features that will not
 *-------------------------------------------------------------------------------------------
*/

#include "emu.h"
#include "68561mpcc.h"

//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************
//#define LOG_GENERAL (1U <<  0)
#define LOG_SETUP   (1U <<  1)
#define LOG_INT     (1U <<  2)
#define LOG_READ    (1U <<  4)
#define LOG_CMD     (1U <<  5)
#define LOG_TX      (1U <<  6)
#define LOG_RCV     (1U <<  7)
#define LOG_CTS     (1U <<  8)
#define LOG_DCD     (1U <<  9)
#define LOG_SYNC    (1U <<  10)
#define LOG_CHAR    (1U <<  11)
#define LOG_RX      (1U <<  12)

//#define VERBOSE ( LOG_SETUP | LOG_GENERAL | LOG_INT)
//#define LOG_OUTPUT_FUNC printf

#include "logmacro.h"

//#define LOG(...)      LOGMASKED(LOG_GENERAL, __VA_ARGS__)
#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 LOGCHAR(...)  LOGMASKED(LOG_CHAR,    __VA_ARGS__)
#define LOGRX(...)    LOGMASKED(LOG_RX,      __VA_ARGS__)

#ifdef _MSC_VER
#define FUNCNAME __func__
#define LLFORMAT "%I64d"
#else
#define FUNCNAME __PRETTY_FUNCTION__
#define LLFORMAT "%lld"
#endif

//**************************************************************************
//  DEVICE DEFINITIONS
//**************************************************************************
// device type definition
const device_type MPCC       = &device_creator<mpcc_device>;
const device_type MPCC68560  = &device_creator<mpcc68560_device>;
const device_type MPCC68560A = &device_creator<mpcc68560A_device>;
const device_type MPCC68561  = &device_creator<mpcc68561_device>;
const device_type MPCC68561A = &device_creator<mpcc68561A_device>;

//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

mpcc_device::mpcc_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_serial_interface(mconfig, *this),
	  m_irq(CLEAR_LINE),
	  m_variant(variant),
	  m_rxc(0),
	  m_txc(0),
	  m_brg_rate(0),
	  m_rcv(0),
	  m_rxd(0),
	  m_tra(0),
	  m_out_txd_cb(*this),
	  m_out_dtr_cb(*this),
	  m_out_rts_cb(*this),
	  m_out_rtxc_cb(*this),
	  m_out_trxc_cb(*this),
	  m_out_int_cb(*this),
	  m_rsr(0),
	  m_rcr(0),
	  m_rdr(0),
	  m_rivnr(0),
	  m_rier(0),
	  m_tsr(0),
	  m_tcr(0),
	  m_tdr(0),
	  m_tivnr(0),
	  m_tier(0),
	  m_sisr(0),
	  m_sicr(0),
	  m_sivnr(0),
	  m_sier(0),
	  m_psr1(0),
	  m_psr2(0),
	  m_ar1(0),
	  m_ar2(0),
	  m_brdr1(0),
	  m_brdr2(0),
	  m_ccr(0),
	  m_ecr(0)
{
	for (auto & elem : m_int_state)
		elem = 0;
}

mpcc_device::mpcc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, MPCC, "Rockwell MPCC", tag, owner, clock, "mpcc", __FILE__),
	  device_serial_interface(mconfig, *this),
	  m_variant(TYPE_MPCC),
	  m_rxc(0),
	  m_txc(0),
	  m_brg_rate(0),
	  m_rcv(0),
	  m_rxd(0),
	  m_tra(0),
	  m_out_txd_cb(*this),
	  m_out_dtr_cb(*this),
	  m_out_rts_cb(*this),
	  m_out_rtxc_cb(*this),
	  m_out_trxc_cb(*this),
	  m_out_int_cb(*this),
	  m_rsr(0),
	  m_rcr(0),
	  m_rdr(0),
	  m_rivnr(0),
	  m_rier(0),
	  m_tsr(0),
	  m_tcr(0),
	  m_tdr(0),
	  m_tivnr(0),
	  m_tier(0),
	  m_sisr(0),
	  m_sicr(0),
	  m_sivnr(0),
	  m_sier(0),
	  m_psr1(0),
	  m_psr2(0),
	  m_ar1(0),
	  m_ar2(0),
	  m_brdr1(0),
	  m_brdr2(0),
	  m_ccr(0),
	  m_ecr(0)
{
	for (auto & elem : m_int_state)
		elem = 0;
}

mpcc68560_device::mpcc68560_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: mpcc_device(mconfig, MPCC68560, "MPCC 68560", tag, owner, clock, TYPE_MPCC68560, "mpcc68560", __FILE__){ }

mpcc68560A_device::mpcc68560A_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: mpcc_device(mconfig, MPCC68560A, "MPCC 68560A", tag, owner, clock, TYPE_MPCC68560A, "mpcc68560A", __FILE__){ }

mpcc68561_device::mpcc68561_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: mpcc_device(mconfig, MPCC68561, "MPCC 68561", tag, owner, clock, TYPE_MPCC68561, "mpcc68561", __FILE__){ }

mpcc68561A_device::mpcc68561A_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: mpcc_device(mconfig, MPCC68561A, "MPCC 68561A", tag, owner, clock, TYPE_MPCC68561A, "mpcc68561A", __FILE__){ }

//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------
void mpcc_device::device_start()
{
	LOGSETUP("%s\n", FUNCNAME);

	// resolve callbacks
	m_out_txd_cb.resolve_safe();
	m_out_dtr_cb.resolve_safe();
	m_out_rts_cb.resolve_safe();
	m_out_rtxc_cb.resolve_safe();
	m_out_trxc_cb.resolve_safe();
	m_out_int_cb.resolve_safe();

	// state saving
	save_item(NAME(m_int_state));
	save_item(NAME(m_rsr));
	save_item(NAME(m_rcr));
	save_item(NAME(m_rdr));
	save_item(NAME(m_rivnr));
	save_item(NAME(m_rier));
	save_item(NAME(m_tsr));
	save_item(NAME(m_tcr));
	save_item(NAME(m_tdr));
	save_item(NAME(m_tivnr));
	save_item(NAME(m_tier));
	save_item(NAME(m_sisr));
	save_item(NAME(m_sicr));
	save_item(NAME(m_sivnr));
	save_item(NAME(m_sier));
	save_item(NAME(m_psr1));
	save_item(NAME(m_psr2));
	save_item(NAME(m_ar1));
	save_item(NAME(m_ar2));
	save_item(NAME(m_brdr1));
	save_item(NAME(m_brdr2));
	save_item(NAME(m_ccr));
	save_item(NAME(m_ecr));
	LOG(" - MPCC variant %02x\n", m_variant);

	device_serial_interface::register_save_state(machine().save(), this);
}

//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------
void mpcc_device::device_reset()
{
	LOGSETUP("%s %s \n",tag(), FUNCNAME);

	// Reset RS232 emulation
	receive_register_reset();
	transmit_register_reset();

	// Device reset values
	m_rsr   = 0x00;
	m_rcr   = 0x01;
	m_rivnr = 0x0f;
	m_rier  = 0x00;
	m_tsr   = 0x80;
	m_tcr   = 0x01;
	m_tivnr = 0x0f;
	m_tier  = 0x00;
	m_sisr  = 0x00;
	m_sicr  = 0x00;
	m_sivnr = 0x0f;
	m_sier  = 0x00;
	m_psr1  = 0x00;
	m_psr2  = 0x00;
	m_ar1   = 0x00;
	m_ar2   = 0x00;
	m_brdr1 = 0x01;
	m_brdr2 = 0x00;
	m_ccr   = 0x00;
	m_ecr   = 0x04;

	// Clear fifos
	m_tx_data_fifo.clear();
	m_rx_data_fifo.clear();

	// Init out callbacks to known inactive state
	m_out_txd_cb(1);
	m_out_dtr_cb(CLEAR_LINE);
	m_out_rts_cb(CLEAR_LINE);
	m_out_rtxc_cb(CLEAR_LINE);
	m_out_trxc_cb(CLEAR_LINE);
	m_out_int_cb(CLEAR_LINE);
	m_irq = CLEAR_LINE;
}

/*
 * Serial device implementation
 */
void mpcc_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	device_serial_interface::device_timer(timer, id, param, ptr);
}

WRITE_LINE_MEMBER(mpcc_device::cts_w)
{
	if (state == CLEAR_LINE)
	{
		uint8_t old_sisr = m_sisr;

		m_sisr &= ~REG_SISR_CTSLVL;
		if ( (old_sisr & REG_SISR_CTSLVL) &&
			 (m_sicr & REG_SICR_RTSLVL) &&
			 (m_tcr  & REG_TCR_TEN))
		{
			m_sisr |= REG_SISR_CTST;
			if (m_sier & REG_SIER_CTS)
			{
				// TODO: make sure interrupt is issued with the next negative transition of TxC
				trigger_interrupt(INT_SR_CTS);
				// TODO: Make sure TxC has negative transition after CTS goes inactive before INT can be reset in SISR7
			}
		}
	}
	else
		m_sisr |= REG_SISR_CTSLVL;
}

WRITE_LINE_MEMBER(mpcc_device::dsr_w)
{
	if (state == ASSERT_LINE)
	{
		uint8_t old_sisr = m_sisr;

		m_sisr |= REG_SISR_DSRLVL;
		if ( !(old_sisr & REG_SISR_DSRLVL) &&
			 !(m_rcr  & REG_RCR_RRES))
		{
			m_sisr |= REG_SISR_DSRT;
			if (m_sier & REG_SIER_DSR)
			{
				// TODO: make sure interrupt is issued with the next negative transition of RxC
				trigger_interrupt(INT_SR_DSR);
				// TODO: Make sure RxC has negative transition after DSR goes inactive before INT can be reset in SISR6
			}
		}
	}
	else
		m_sisr &= ~REG_SISR_DSRLVL;
}

WRITE_LINE_MEMBER(mpcc_device::dcd_w)
{
	if (state == CLEAR_LINE)
	{
		uint8_t old_sisr = m_sisr;

		m_sisr &= ~REG_SISR_DCDLVL;
		if ( (old_sisr & REG_SISR_DCDLVL) &&
			 !(m_rcr & REG_RCR_RRES))
		{
			m_sisr |= REG_SISR_DCDT;
			if (m_sier & REG_SIER_DCD)
			{
				// TODO: make sure interrupt is issued with the next negative transition of RxC
				trigger_interrupt(INT_SR_DCD);
				// TODO: Make sure RxC has negative transition before INT can be reset in SISR5
			}
		}
	}
	else
		m_sisr |= REG_SISR_DCDLVL;
}

//-------------------------------------------------
// get_brg_rate - helper function
//-------------------------------------------------
uint32_t mpcc_device::get_brg_rate()
{
	uint32_t rate;
	uint32_t brg_const;

	brg_const = (m_brdr1 | m_brdr2 << 8); // Baud rate divider
	brg_const += (m_ccr & REG_CCR_PSCDIV) ? 3 : 2; // Add prescaler factor
	brg_const += (m_psr2 & REG_PSR2_PSEL_MSK) == REG_PSR2_PSEL_ASCII ? 2 : 1; // Add K factor
	rate = clock() / brg_const;

	return rate;
}

//-------------------------------------------------
// get_tx_rate - helper function
//-------------------------------------------------
uint32_t mpcc_device::get_tx_rate()
{
	uint32_t rate;

	// Check if TxC is an input and use it instead of the BRG
	if ((m_ccr & REG_CCR_TCLO) == 0)
	{
		rate = m_txc;
	}
	else
	{
		rate = get_brg_rate();
	}

	return rate;
}

//-------------------------------------------------
// get_clock_div - helper function
//-------------------------------------------------
uint32_t mpcc_device::get_clock_div()
{
	uint32_t clk_div = 1;

	switch (m_ccr & REG_CCR_CLKDIV_MSK)
	{
	case REG_CCR_CLKDIV_X1 : clk_div =  1; break;
	case REG_CCR_CLKDIV_X16: clk_div = 16; break;
	case REG_CCR_CLKDIV_X32: clk_div = 32; break;
	case REG_CCR_CLKDIV_X64: clk_div = 64; break;
	}

	return clk_div;
}

//-------------------------------------------------
// get_rx_rate - helper function
//-------------------------------------------------
uint32_t mpcc_device::get_rx_rate()
{
	uint32_t rate;

	// Check if TxC is an input and use it instead of the BRG
	if ((m_ccr & REG_CCR_RCLKIN) == 0)
	{
		rate = m_rxc / get_clock_div();
	}
	else
	{
		rate = get_brg_rate();
	}

	return rate;
}

//-------------------------------------------------
//  get_word_length - get word length
//-------------------------------------------------
uint32_t mpcc_device::get_word_length()
{
	int bits = 5;

	switch (m_psr2 & REG_PSR2_CHLN_MSK)
	{
	case REG_PSR2_CHLN_5:  bits = 5;   break;
	case REG_PSR2_CHLN_6:  bits = 6;   break;
	case REG_PSR2_CHLN_7:  bits = 7;   break;
	case REG_PSR2_CHLN_8:  bits = 8;   break;
	}

	return bits;
}

//-------------------------------------------------
//  get_stop_bits - translate stop bit settings for serial interface
//-------------------------------------------------
device_serial_interface::stop_bits_t mpcc_device::get_stop_bits()
{
	switch (m_psr2 & REG_PSR2_STP_MSK)
	{
	case REG_PSR2_STP_1:   return STOP_BITS_1;
	case REG_PSR2_STP_1_5: return STOP_BITS_1_5;
	case REG_PSR2_STP_2:   return STOP_BITS_2;
	}

	return STOP_BITS_0;
}

//-------------------------------------------------
//  get_parity - translate parity settings for serial interface
//-------------------------------------------------
device_serial_interface::parity_t mpcc_device::get_parity()
{
	parity_t parity;

	if (m_ecr & REG_ECR_PAREN)
	{
		if (m_ecr & REG_ECR_ODDPAR)
			parity = PARITY_ODD;
		else
			parity = PARITY_EVEN;
	}
	else
	{
		parity = PARITY_NONE;
	}
	return parity;
}

//-------------------------------------------------
//  update_serial -
//-------------------------------------------------
void mpcc_device::update_serial()
{
	int         data_bits = get_word_length();
	stop_bits_t stop_bits = get_stop_bits();
	parity_t    parity    = get_parity();

	LOGSETUP(" %s() %s Setting data frame %d+%d%c%s\n", FUNCNAME, m_owner->tag(), 1,
		 data_bits, parity == PARITY_NONE ? 'N' : parity == PARITY_EVEN ? 'E' : 'O',
		stop_bits == STOP_BITS_1 ? "1" : (stop_bits == STOP_BITS_2 ? "2" : "1.5"));

	set_data_frame(1, data_bits, parity, stop_bits);

	// Setup the Receiver
	//  check if the receiver is in reset mode
	if  (m_rcr & REG_RCR_RRES)
	{
		LOGSETUP("- Rx in reset\n");
		set_rcv_rate(0);
	}
	// Rx is running
	else
	{
		LOGSETUP("- Rx enabled\n");
		m_brg_rate = get_rx_rate();

		LOGSETUP("- BRG rate %d\n", m_brg_rate);
		set_rcv_rate(m_brg_rate);
	}

	// Setup the Transmitter
	//  check if Rx is in reset
	if  (m_tcr & REG_TCR_TRES)
	{
		LOGSETUP("- Tx in reset\n");
		set_tra_rate(0);
	}
	// Tx is running
	else
	{
		// Check that Tx is enabled
		if (m_tcr & REG_TCR_TEN)
		{
			LOGSETUP("- Tx enabled\n");
			m_brg_rate = get_tx_rate();

			LOGSETUP("- BRG rate %d\n", m_brg_rate);
			set_tra_rate(m_brg_rate);
		}
		else
		{
			LOGSETUP("- Tx disabled\n");
			set_tra_rate(0);
		}
	}
}

//-------------------------------------------------
//  tra_callback - is called for each bit that needs to be transmitted
//-------------------------------------------------
void mpcc_device::tra_callback()
{
	// Check if transmitter is idle as in disabled
	if (!(m_tcr & REG_TCR_TEN))
	{
		// transmit idle TODO: Support TCR TICS bit
		LOGTX("%s idle bit\n", FUNCNAME);
		m_out_txd_cb(1);
	}
#if 0
	// Check if we are transmitting break TODO: Figure out Break support
	else if (...)
	{
		// transmit break
		m_out_txd_cb(0);
	}
#endif
	// Check if there is more bits to send
	else if (!is_transmit_register_empty())
	{
		// Get the next bit
		int db = transmit_register_get_data_bit();
		LOGTX("%s bit: %d\n", FUNCNAME, db ? 1 : 0);

		// transmit data
		m_out_txd_cb(db);
	}
	// Otherwise we don't know why we are called...
	else
	{
		logerror("%s %s Failed to transmit\n", FUNCNAME, m_owner->tag());
	}
}

//-------------------------------------------------
//  tra_complete - is called when the transmitter shift register has sent the last bit
//-------------------------------------------------
void mpcc_device::tra_complete()
{
	// check if transmitter is enabled and we are not sending BREAK level
	if ((m_tcr & REG_TCR_TEN) && !(m_tcr & REG_TCR_TICS))
	{   // check if there are more data in the fifo
		if (!m_tx_data_fifo.empty())
		{
			transmit_register_setup(m_tx_data_fifo.dequeue()); // Reload the shift register
			m_tsr |= REG_TSR_TDRA; // Mark fifo as having room for more data
		}
		else
		{
			m_out_rts_cb(CLEAR_LINE); // TODO: respect the RTSLV bit
			m_sicr &= ~REG_SICR_RTSLVL;
		}

		// Check if Tx interrupts are enabled
		if (m_tier & REG_TIER_TDRA)
		{
			trigger_interrupt(INT_TX_TDRA);
		}
	}       // Check if sending BREAK
	else if (m_tcr & REG_TCR_TICS)
	{
		// TODO: Should transmit content of AR2, needs investigation
		m_out_txd_cb(0);
	}
	else
	{
		// transmit mark
		m_out_txd_cb(1);
	}
}

//-------------------------------------------------
//  rcv_callback - called when it is time to sample incomming data bit
//-------------------------------------------------
void mpcc_device::rcv_callback()
{
	// Check if the Receiver is enabled
	if (!(m_rcr & REG_RCR_RRES))
	{
		receive_register_update_bit(m_rxd);
	}
}

//-------------------------------------------------
//  rcv_complete -
//-------------------------------------------------
void mpcc_device::rcv_complete()
{
	uint8_t data;

	receive_register_extract();
	data = get_received_char();
	LOGRX("%s %02x [%c]\n", FUNCNAME, isascii(data) ? data : ' ', data);

	//  receive_data(data);
	if (m_rx_data_fifo.full())
	{
		// receive overrun error detected, new data is lost
		m_rsr |= REG_RSR_ROVRN;
		// interrupt if rx overrun interrupt is enabled
		if (m_rier & REG_RIER_ROVRN)
		{
			trigger_interrupt(INT_RX_ROVRN);
		}
	}
	else
	{
		m_rx_data_fifo.enqueue(data);
		m_rsr |= REG_RSR_RDA;
		// interrupt if rx data availble is enabled
		if (m_rier & REG_RIER_RDA)
		{
			trigger_interrupt(INT_RX_RDA);
		}
	}
}

//-------------------------------------------------
//  write_rx - called by terminal through rs232/diserial
//         when character is sent to board
//-------------------------------------------------
WRITE_LINE_MEMBER(mpcc_device::write_rx)
{
	LOGRCV("%s(%d)\n", FUNCNAME, state);
	m_rxd = state;

	//only use rx_w when self-clocked
	if(m_rxc != 0 || m_brg_rate != 0)
		device_serial_interface::rx_w(state);
}


/*
 * Interrupts
 */
//-------------------------------------------------
//  check_interrupts -
//-------------------------------------------------
void mpcc_device::check_interrupts()
{
	int state = 0;
	LOGINT("%s %s \n",tag(), FUNCNAME);

	// loop over all interrupt sources
	for (auto & elem : m_int_state)
	{
		state |= elem;
	}

	// update IRQ line
	// If we are not serving any interrupt we need to check for a new interrupt or
	//   otherwise the IRQ line is asserted already and we need to do nothing
	if ((state & INT_ACK) == 0)
	{
		// If there is a new interrupt not yet acknowledged IRQ needs to be asserted
		if (state & INT_REQ)
		{
			if (m_irq != ASSERT_LINE)
			{
				m_out_int_cb(ASSERT_LINE);
				m_irq = ASSERT_LINE;
			}
		}
		// Otherwise we just clear the IRQ line allowing other devices to interrupt
		else
		{
			if (m_irq != CLEAR_LINE)
			{
				m_out_int_cb(CLEAR_LINE);
				m_irq = CLEAR_LINE;
			}
		}
	}
}

//-------------------------------------------------
//  reset_interrupts -
//-------------------------------------------------

void mpcc_device::reset_interrupts()
{
	LOGINT("%s %s \n",tag(), FUNCNAME);
	// reset internal interrupt sources
	for (auto & elem : m_int_state)
	{
		elem = 0;
	}

	// check external interrupt sources
	check_interrupts();
}

//-----------------------------------------------------------------------
//  trigger_interrupt - called when a potential interrupt condition occurs
//-------------------------------------------------
void mpcc_device::trigger_interrupt(int source)
{
	LOGINT("%s %s: %02x\n",FUNCNAME, tag(), source);
	switch(source)
	{
	case INT_TX_TDRA:
	case INT_TX_TFC:
	case INT_TX_TUNRN:
	case INT_TX_TFERR:
		m_int_state[TX_INT_PRIO] = INT_REQ;
		break;
	case INT_RX_RDA:
	case INT_RX_EOF:
	case INT_RX_CPERR:
	case INT_RX_FRERR:
	case INT_RX_ROVRN:
	case INT_RX_RAB:
		m_int_state[RX_INT_PRIO] = INT_REQ;
		break;
	case INT_SR_CTS:
	case INT_SR_DSR:
	case INT_SR_DCD:
		m_int_state[SR_INT_PRIO] = INT_REQ;
		break;
	}
	check_interrupts();
}

//-------------------------------------------------------------------------
//  update_interrupt - called when an interrupt condition has been cleared
//-------------------------------------------------------------------------
void mpcc_device::update_interrupts(int source)
{
	LOGINT("%s %s \n",FUNCNAME, tag());

	switch(source)
	{
	case INT_TX_TDRA:
	case INT_TX_TFC:
	case INT_TX_TUNRN:
	case INT_TX_TFERR:
		if ( m_tsr & (REG_TSR_TDRA | REG_TSR_TFC | REG_TSR_TUNRN | REG_TSR_TFERR) )
		{
			LOGINT(" - Found unserved TX interrupt %02x\n", m_tsr);
			m_int_state[TX_INT_PRIO] = INT_REQ; // Still TX interrupts to serve
		}
		else
		{
			m_int_state[TX_INT_PRIO] = INT_NONE; // No more TX interrupts to serve
		}
		break;
	case INT_RX_RDA:
	case INT_RX_EOF:
	case INT_RX_CPERR:
	case INT_RX_FRERR:
	case INT_RX_ROVRN:
	case INT_RX_RAB:
		if ( m_rsr & (REG_RSR_RDA | REG_RSR_EOF | REG_RSR_CPERR | REG_RSR_FRERR | REG_RSR_ROVRN | REG_RSR_RAB))
		{
			LOGINT(" - Found unserved RX interrupt %02x\n", m_rsr);
			m_int_state[RX_INT_PRIO] = INT_REQ; // Still RX interrupts to serve
		}
		else
		{
			m_int_state[RX_INT_PRIO] = INT_NONE; // No more RX interrupts to serve
		}
		break;
	case INT_SR_CTS:
	case INT_SR_DSR:
	case INT_SR_DCD:
		if ( m_sisr & (REG_SISR_CTST | REG_SISR_DSRT | REG_SISR_DCDT ) )
		{
			LOGINT(" - Found unserved SR interrupt %02x\n", m_sisr);
			m_int_state[SR_INT_PRIO] = INT_REQ; // Still SR interrupts to serve
		}
		else
		{
			m_int_state[SR_INT_PRIO] = INT_NONE; // No more SR interrupts to serve
		}
		break;
	}
	check_interrupts();
}

//-------------------------------------------------
//  Read register
//-------------------------------------------------
READ8_MEMBER( mpcc_device::read )
{
	uint8_t data = 0;

	switch(offset)
	{
	case 0x00: data = do_rsr(); break;
	case 0x01: data = do_rcr(); break;
	case 0x02: data = do_rdr(); break;
	case 0x04: data = do_rivnr(); break;
	case 0x05: data = do_rier(); break;
	case 0x08: data = do_tsr(); break;
	case 0x09: data = do_tcr(); break;
	//case 0x0a: data = m_tdr; break; // TDR is a write only register
	case 0x0c: data = do_tivnr(); break;
	case 0x0d: data = do_tier(); break;
	case 0x10: data = do_sisr(); break;
	case 0x11: data = do_sicr(); break;
	case 0x14: data = m_sivnr; logerror("MPCC: Reg SIVNR not implemented\n"); break;
	case 0x15: data = do_sier(); break;
	case 0x18: data = do_psr1(); break;
	case 0x19: data = do_psr2(); break;
	case 0x1a: data = m_ar1; logerror("MPCC: Reg AR1 not implemented\n"); break;
	case 0x1b: data = m_ar2; logerror("MPCC: Reg AR2 not implemented\n"); break;
	case 0x1c: data = do_brdr1(); break;
	case 0x1d: data = do_brdr2(); break;
	case 0x1e: data = do_ccr(); break;
	case 0x1f: data = do_ecr(); break;
	default: logerror("%s:%s invalid register accessed: %02x\n", m_owner->tag(), tag(), offset);
	}
	LOGR(" * %s Reg %02x -> %02x  \n", tag(), offset, data);
	return data;
}

//-------------------------------------------------
//  Write register
//-------------------------------------------------
WRITE8_MEMBER( mpcc_device::write )
{
	LOGSETUP(" * %s Reg %02x <- %02x  \n", tag(), offset, data);
	switch(offset)
	{
	case 0x00: do_rsr(data); break;
	case 0x01: do_rcr(data); break;
	//case 0x02: m_rdr = data; break; // RDR is a read only register
	case 0x04: do_rivnr(data); break;
	case 0x05: do_rier(data); break;
	case 0x08: do_tsr(data); break;
	case 0x09: do_tcr(data); break;
	case 0x0a: m_tdr = data; LOGCHAR("*%c", data); do_tdr(data); break;
	case 0x0c: do_tivnr(data); break;
	case 0x0d: do_tier(data); break;
	case 0x10: do_sisr(data); break;
	case 0x11: do_sicr(data); break;
	case 0x14: m_sivnr = data; logerror("MPCC: Reg SIVNR not implemented\n"); break;
	case 0x15: do_sier(data); break;
	case 0x18: do_psr1(data); break;
	case 0x19: do_psr2(data); break;
	case 0x1a: m_ar1 = data; logerror("MPCC: Reg AR1 not implemented\n"); break;
	case 0x1b: m_ar2 = data; logerror("MPCC: Reg AR2 not implemented\n"); break;
	case 0x1c: do_brdr1(data); break;
	case 0x1d: do_brdr2(data); break;
	case 0x1e: do_ccr(data); break;
	case 0x1f: do_ecr(data); break;
	default: logerror("%s:%s invalid register accessed: %02x\n", m_owner->tag(), tag(), offset);
	}
}

// TODO: Sync clear of error bits with readout from fifo
// TODO: implement Idle bit
void mpcc_device::do_rsr(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	m_rsr = data;
	update_interrupts(INT_RX);
}

uint8_t mpcc_device::do_rsr()
{
	uint8_t data = m_rsr;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

void mpcc_device::do_rcr(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	m_rcr = data;
	LOGSETUP(" - Rx DMA      : %s\n", (m_rcr & REG_RCR_RDSREN) ? "enabled" : "disabled");
	LOGSETUP(" - Rx DONE out : %s\n", (m_rcr & REG_RCR_DONEEN) ? "enabled" : "disabled");
	LOGSETUP(" - Rx RSYN out : %s\n", (m_rcr & REG_RCR_RSYNEN) ? "enabled" : "disabled");
	LOGSETUP(" - Rx strip SYN: %s\n", (m_rcr & REG_RCR_STRSYN) ? "enabled" : "disabled");
	LOGSETUP(" - Rx Abort    : %s\n", (m_rcr & REG_RCR_RABTEN) ? "enabled" : "disabled");
	LOGSETUP(" - Rx Mode     : %s\n", (m_rcr & REG_RCR_RRES  ) ? "reset" : "normal");

	update_serial();
}

uint8_t mpcc_device::do_rcr()
{
	uint8_t data = m_rcr;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

uint8_t mpcc_device::do_rdr()
{
	uint8_t data = 0;

	if (!m_rx_data_fifo.empty())
	{
		// load data from the FIFO
		data = m_rx_data_fifo.dequeue();

		// Check if this was the last data and reset the interrupt and status register accordingly
		if (m_rx_data_fifo.empty())
		{
			m_rsr &= ~REG_RSR_RDA;
			update_interrupts(INT_RX_RDA);
		}
	}
	else
	{
		LOGRX("data_read: Attempt to read out character from empty FIFO\n");
		logerror("data_read: Attempt to read out character from empty FIFO\n");
	}

	LOGRX("%s <- %02x [%c]\n", FUNCNAME, isascii(data) ? data : ' ', data);
	return data;
}

void mpcc_device::do_rivnr(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	m_rivnr = data;
	LOGSETUP(" - Rx Int vector: %02x\n", m_tivnr);
}

uint8_t mpcc_device::do_rivnr()
{
	uint8_t data = m_rivnr;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

void mpcc_device::do_rier(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	m_rier = data;
	LOGSETUP(" - Rx INT on Rx data available    : %s\n", (m_rier & REG_RIER_RDA)   ? "enabled" : "disabled");
	LOGSETUP(" - Rx INT on End of Frame         : %s\n", (m_rier & REG_RIER_EOF)   ? "enabled" : "disabled");
	LOGSETUP(" - Rx INT on CRC/Parity error     : %s\n", (m_rier & REG_RIER_CPERR) ? "enabled" : "disabled");
	LOGSETUP(" - Rx INT on Frame error          : %s\n", (m_rier & REG_RIER_FRERR) ? "enabled" : "disabled");
	LOGSETUP(" - Rx INT on Receiver overrun     : %s\n", (m_rier & REG_RIER_ROVRN) ? "enabled" : "disabled");
	LOGSETUP(" - Rx INT on Abort/Break          : %s\n", (m_rier & REG_RIER_RAB)   ? "enabled" : "disabled");
}

uint8_t mpcc_device::do_rier()
{
	uint8_t data = m_rier;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

void mpcc_device::do_tdr(uint8_t data)
{
	LOG("%s -> %d [%c]\n", FUNCNAME, data, isprint(data) ? data : ' ');
	// Check of Tx fifo has room
	if (m_tx_data_fifo.full())
	{
		logerror("- TX FIFO is full, discarding data\n");
		LOGTX("- TX FIFO is full, discarding data\n");
	}
	else // ..there is still room
	{
		m_tx_data_fifo.enqueue(data);
		if (m_tx_data_fifo.full())
		{
			m_tsr &= ~REG_TSR_TDRA; // Mark fifo as full
			update_interrupts(INT_TX_TDRA);
		}
	}

	// Check if Tx is enabled
	if (m_tcr & REG_TCR_TEN)
	{
		LOGTX("- TX is enabled\n");
		if (is_transmit_register_empty()) // Is the shift register loaded?
		{
			LOGTX("- Setting up transmitter\n");
			transmit_register_setup(m_tx_data_fifo.dequeue()); // Load the shift register, reload is done in tra_complete()
			m_tsr |= REG_TSR_TDRA; // Now there is a slot in the FIFO available again
			if (m_tier & REG_TIER_TDRA)
			{
				trigger_interrupt(INT_TX_TDRA);
			}
		}
		else
		{
			LOGTX("- Transmitter not empty\n");
		}
	}
}

void mpcc_device::do_tsr(uint8_t data)
{
	LOGINT("%s -> %02x\n", FUNCNAME, data);
	m_tsr = data;
	update_interrupts(INT_TX);
}

uint8_t mpcc_device::do_tsr()
{
	uint8_t data = m_tsr;
	LOGR("%s <- %02x\n", FUNCNAME, data);
	return data;
}

void mpcc_device::do_tcr(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	m_tcr = data;
	LOGSETUP(" - Tx                : %s\n", (m_tcr & REG_TCR_TEN)    ? "enabled" : "disabled");
	LOGSETUP(" - Tx DMA            : %s\n", (m_tcr & REG_TCR_TDSREN) ? "enabled" : "disabled");
	LOGSETUP(" - Tx Idle character : %s\n", (m_tcr & REG_TCR_TICS)   ? "AR2"     : "high");
	LOGSETUP(" - Tx Half Word next : %s\n", (m_tcr & REG_TCR_THW)    ? "yes"     : "no");
	LOGSETUP(" - Tx Last character : %s\n", (m_tcr & REG_TCR_TLAST)  ? "yes"     : "no");
	LOGSETUP(" - Tx SYN            : %s\n", (m_tcr & REG_TCR_TSYN)   ? "enabled" : "disabled");
	LOGSETUP(" - Tx Abort command  : %s\n", (m_tcr & REG_TCR_TABT)   ? "active"  : "inactive");
	LOGSETUP(" - Tx Mode           : %s\n", (m_tcr & REG_TCR_TRES)   ? "reset"   : "normal");

	update_serial();
}

uint8_t mpcc_device::do_tcr()
{
	uint8_t data = m_tcr;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

void mpcc_device::do_tivnr(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	m_tivnr = data;
	LOGSETUP(" - Tx Int vector: %02x\n", m_tivnr);
}

uint8_t mpcc_device::do_tivnr()
{
	uint8_t data = m_tivnr;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

void mpcc_device::do_tier(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	m_tier = data;
	LOGSETUP(" - Tx INT on FIFO slot available  : %s\n", (m_tier & REG_TIER_TDRA) ? "enabled" : "disabled");
	LOGSETUP(" - Tx INT on Frame complete       : %s\n", (m_tier & REG_TIER_TFC ) ? "enabled" : "disabled");
	LOGSETUP(" - Tx INT on Underrun             : %s\n", (m_tier & REG_TIER_TUNRN) ? "enabled" : "disabled");
	LOGSETUP(" - Tx INT on Frame error          : %s\n", (m_tier & REG_TIER_TFERR) ? "enabled" : "disabled");
}

uint8_t mpcc_device::do_tier()
{
	uint8_t data = m_tier;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

void mpcc_device::do_sisr(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	if (data & REG_SISR_CTST) m_sisr &= ~REG_SISR_CTST;
	if (data & REG_SISR_DSRT) m_sisr &= ~REG_SISR_DSRT;
	if (data & REG_SISR_DCDT) m_sisr &= ~REG_SISR_DCDT;
	update_interrupts(INT_SR);

	LOGSETUP(" - CTS %d transitioned: %d\n", (m_sisr & REG_SISR_CTSLVL) ? 1 :0, (m_sisr & REG_SISR_CTST) ? 1 : 0);
	LOGSETUP(" - DSR %d transitioned: %d\n", (m_sisr & REG_SISR_DSRLVL) ? 1 :0, (m_sisr & REG_SISR_DSRT) ? 1 : 0);
	LOGSETUP(" - DCD %d transitioned: %d\n", (m_sisr & REG_SISR_DCDLVL) ? 1 :0, (m_sisr & REG_SISR_DCDT) ? 1 : 0);
}

uint8_t mpcc_device::do_sisr()
{
	uint8_t data = m_sisr;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

void mpcc_device::do_sicr(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);

	// If RTS is activated the RTS output latch can only be reset by an empty FIFO.
	if ( !(m_sicr & REG_SICR_RTSLVL) &&
		 (data & REG_SICR_RTSLVL))
	{
		m_out_rts_cb(ASSERT_LINE); // TODO: respect the RTSLV bit
	}

	m_sicr = data;

	if (m_sicr & REG_SICR_DTRLVL)
	{
		m_out_dtr_cb(ASSERT_LINE);
	}
	else
	{
		m_out_dtr_cb(CLEAR_LINE);
	}

	LOGSETUP(" - RTS level : %s\n", (m_sicr & REG_SICR_RTSLVL) ? "high" : "low");
	LOGSETUP(" - DTR level : %s\n", (m_sicr & REG_SICR_DTRLVL) ? "high" : "low");
	LOGSETUP(" - Echo Mode : %s\n", (m_sicr & REG_SICR_ECHO)   ? "enabled" : "disabled");
	LOGSETUP(" - Test Mode : %s\n", (m_sicr & REG_SICR_TEST)   ? "enabled" : "disabled");
}

uint8_t mpcc_device::do_sicr()
{
	uint8_t data = m_sicr;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

void mpcc_device::do_sier(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	m_sier = data;
	LOGSETUP(" - Serial interface INT on CTS: %s\n", (m_sier & REG_SIER_CTS) ? "enabled" : "disabled");
	LOGSETUP(" - Serial interface INT on DSR: %s\n", (m_sier & REG_SIER_DSR) ? "enabled" : "disabled");
	LOGSETUP(" - Serial interface INT on DCD: %s\n", (m_sier & REG_SIER_DCD) ? "enabled" : "disabled");
}

uint8_t mpcc_device::do_sier()
{
	uint8_t data = m_sier;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

void mpcc_device::do_psr1(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	m_psr1 = data;
	LOGSETUP(" - Zero Address option: %s\n", (m_psr1 & REG_PSR1_ADRZ)  ? "enabled" : "disabled" );
	LOGSETUP(" - IPARS option       : %s\n", (m_psr1 & REG_PSR1_IPARS) ? "enabled" : "disabled" );
	LOGSETUP(" - Control Field Width: %s\n", (m_psr1 & REG_PSR1_CTLEX) ? "16 bit"  : "8 bit" );
	LOGSETUP(" - Address Extend     : %s\n", (m_psr1 & REG_PSR1_ADDEX) ? "enabled" : "disabled" );

	update_serial();
}

uint8_t mpcc_device::do_psr1()
{
	uint8_t data = m_psr1;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

void mpcc_device::do_psr2(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	m_psr2 = data;
	LOGSETUP(" - %s data bus\n", (m_psr2 & REG_PSR2_WDBYT) ? "16 bit (not implemented)" : "8 bit" );
	LOGSETUP(" - %s stop bits\n",(m_psr2 & REG_PSR2_STP_MSK) == REG_PSR2_STP_1 ? "1" :
		( (m_psr2 & REG_PSR2_STP_MSK) == REG_PSR2_STP_1_5 ? "1.5" :
		  ( (m_psr2 & REG_PSR2_STP_MSK) == REG_PSR2_STP_2 ? "2" : "Unknown")));
	LOGSETUP(" - %d bit characters\n", 5 + ((m_psr2 & REG_PSR2_CHLN_MSK) >> 3));
	LOGSETUP(" - Protocol %d %s\n", m_psr2 & REG_PSR2_PSEL_MSK, (m_psr2 & REG_PSR2_PSEL_MSK) != REG_PSR2_PSEL_ASCII ? "(not implemented)" : "");

	update_serial();
}

uint8_t mpcc_device::do_psr2()
{
	uint8_t data = m_psr2;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

/*
 * Clocks and Baud Rates
 */
void mpcc_device::do_brdr1(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	m_brdr1 = data;
	LOGSETUP(" - Baudrate Divider 1: %02x\n", m_brdr1);

	update_serial();
}

uint8_t mpcc_device::do_brdr1()
{
	uint8_t data = m_brdr1;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

void mpcc_device::do_brdr2(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	m_brdr2 = data;
	LOGSETUP(" - Baudrate Divider 2: %02x\n", m_brdr2);

	update_serial();
}

uint8_t mpcc_device::do_brdr2()
{
	uint8_t data = m_brdr2;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

void mpcc_device::do_ccr(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	m_ccr = data;
	LOGSETUP(" - Prescaler: x%d\n", (m_ccr & REG_CCR_PSCDIV) ? 3 : 2);
	LOGSETUP(" - TxC used as: %s\n", (m_ccr & REG_CCR_TCLO) ? "output" : "input");
	LOGSETUP(" - RxC taken from: %s source, (ASYNC mode only)\n", (m_ccr & REG_CCR_TCLO) ? "internal" : "external");
	LOGSETUP(" - External RxC divisor: x%d\n",(m_ccr & REG_CCR_CLKDIV_MSK) == REG_CCR_CLKDIV_X1 ? 1 :
			 ( (m_ccr & REG_CCR_CLKDIV_MSK) == REG_CCR_CLKDIV_X16 ? 16 :
			   ( (m_ccr & REG_CCR_CLKDIV_MSK) == REG_CCR_CLKDIV_X32 ? 32 : 64)));

	update_serial();
}

uint8_t mpcc_device::do_ccr()
{
	uint8_t data = m_ccr;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}

void mpcc_device::do_ecr(uint8_t data)
{
	LOG("%s -> %02x\n", FUNCNAME, data);
	m_ecr = data;
	LOGSETUP(" - Parity         : %s\n", (m_ecr & REG_ECR_PAREN) ? "enabled" : "disabled");
	LOGSETUP(" - Parity         : %s\n", (m_ecr & REG_ECR_ODDPAR) ? "odd" : "even");
	LOGSETUP(" - CRC            : %s\n", (m_ecr & REG_ECR_CFCRC) ? "enabled" : "disabled");
	LOGSETUP(" - CRC Polynominal: %s\n", (m_ecr & REG_ECR_CRCSEL_MSK) == REG_ECR_CRCSEL_V41 ? "CCITT V.41 (BOP)" :
			 ( (m_ecr & REG_ECR_CRCSEL_MSK) == REG_ECR_CRCSEL_C16 ? "CRC-16 (BSC)" :
			   ( (m_ecr & REG_ECR_CRCSEL_MSK) == REG_ECR_CRCSEL_VRC ? "VRC/LRC (BSC, ASCII, non-transp)" :
				 "Not used")));

	update_serial();
}

uint8_t mpcc_device::do_ecr()
{
	uint8_t data = m_ecr;
	LOG("%s <- %02x\n", FUNCNAME, data);
	return data;
}