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

                        



                                                                      
                                                                           
                           
 

                                                                         

 












                                                                            
                                                         
 




                                                                            
                                                            




                                                   
                                                                                                                                                                    
















                                                                                
                                                                                                         
                                                                                       






                                                

                                
                 
                 
                 
                


 
                                                                                                             




                                                                                 
                                                   





                                                   
                                     

                                     


                                         














                                            

                                   
                                                                             




















                                                   
                                    









                                                   
                      
 

                                       


                       




                                                                                  
                                                   








                                                               




                                                   
                                                             






                                                   


                                                                                                                               
 
 




                                                   


                                           
 






                                                   
                      
 

                                       


                       














                                                                                                                                           








































                                                                                              



                                
 
                                                                              
 
                                  












                                                                  
                                 

         
                                                                   








                                                   
 





                                                                       
                         
 


                         


                                  
                    









                                                                 

                      
                                      
                        














                                                   
                                      
 







                                            
         

                                     
 







                                         
 




















                                           


            
                                                            
         
 
 
                  






















                                        
          
 

                                     
 


                                                                                                                   
         

                          
         




                                                                       
 
                                     

                          
                                  
 
 


                                    
 
                           
 




                                          





















                                                                            







                                                                            
                 











                                                          


                    

                                                         

                 


                                           
                 



















                                                      

                 





                                                                      
                 


                                                
                 







                                                                   
                 

                                                

                 












                                                                                         

















                                                    
                                                        
 





                                                     


                    
                                              

                 

         
 




                                                        
                 
                                                    
 













                                                                                               
                 
                                                    
                 



                                               










                                                   
                                               


                                        









                                                   
                         
 
                                              


                                          
                          
 





                                                                     
 
                                     



                                                                       
                          










                                                   
                       


















                                                             
                                                                   



                                           
                         
 
 






                                                                                           
                                          
 
                      
                                         

 

                                          
                      
 


                                     

 

                                          
                       
 

















                                          

                                         

         
// license:BSD-3-Clause
// copyright-holders:smf
/*********************************************************************

    i8251.c

    Intel 8251 Universal Synchronous/Asynchronous Receiver Transmitter code
    NEC uPD71051 is a clone

    The V53/V53A use a customized version with only the Asynchronous mode
    and a split command / mode register



*********************************************************************/

#include "emu.h"
#include "i8251.h"


/***************************************************************************
    MACROS
***************************************************************************/

#define VERBOSE 0

#define LOG(x)  do { if (VERBOSE) logerror x; } while (0)

//**************************************************************************
//  DEVICE DEFINITIONS
//**************************************************************************

const device_type I8251 = &device_creator<i8251_device>;
const device_type V53_SCU = &device_creator<v53_scu_device>;

//-------------------------------------------------
//  i8251_device - constructor
//-------------------------------------------------

i8251_device::i8251_device(const machine_config &mconfig, device_type type, std::string name, std::string tag, device_t *owner, UINT32 clock, std::string shortname)
	: device_t(mconfig, type, name, tag, owner, clock, shortname, __FILE__),
	device_serial_interface(mconfig, *this),
	m_txd_handler(*this),
	m_dtr_handler(*this),
	m_rts_handler(*this),
	m_rxrdy_handler(*this),
	m_txrdy_handler(*this),
	m_txempty_handler(*this),
	m_syndet_handler(*this),
	m_cts(1),
	m_dsr(1),
	m_rxd(0),
	m_rxc(0),
	m_txc(0)
{
}

i8251_device::i8251_device(const machine_config &mconfig, std::string tag, device_t *owner, UINT32 clock)
	: device_t(mconfig, I8251, "8251 USART", tag, owner, clock, "i8251", __FILE__),
	device_serial_interface(mconfig, *this),
	m_txd_handler(*this),
	m_dtr_handler(*this),
	m_rts_handler(*this),
	m_rxrdy_handler(*this),
	m_txrdy_handler(*this),
	m_txempty_handler(*this),
	m_syndet_handler(*this),
	m_cts(1),
	m_dsr(1),
	m_rxd(0),
	m_rxc(0),
	m_txc(0)
{
}

v53_scu_device::v53_scu_device(const machine_config &mconfig, std::string tag, device_t *owner, UINT32 clock)
	: i8251_device(mconfig, V53_SCU, "V53 SCU", tag, owner, clock, "v53_scu")
{
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void i8251_device::device_start()
{
	// resolve callbacks
	m_txd_handler.resolve_safe();
	m_rts_handler.resolve_safe();
	m_dtr_handler.resolve_safe();
	m_rxrdy_handler.resolve_safe();
	m_txrdy_handler.resolve_safe();
	m_txempty_handler.resolve_safe();
	save_item(NAME(m_flags));
	save_item(NAME(m_sync_byte_offset));
	save_item(NAME(m_sync_byte_count));
	save_item(NAME(m_sync_bytes));
	save_item(NAME(m_status));
	save_item(NAME(m_command));
	save_item(NAME(m_mode_byte));
	save_item(NAME(m_cts));
	save_item(NAME(m_dsr));
	save_item(NAME(m_rxd));
	save_item(NAME(m_rxc));
	save_item(NAME(m_txc));
	save_item(NAME(m_rxc_count));
	save_item(NAME(m_txc_count));
	save_item(NAME(m_br_factor));
	save_item(NAME(m_rx_data));
	save_item(NAME(m_tx_data));
	device_serial_interface::register_save_state(machine().save(), this);
}




/*-------------------------------------------------
    update_rx_ready
-------------------------------------------------*/

void i8251_device::update_rx_ready()
{
	int state;

	state = m_status & I8251_STATUS_RX_READY;

	/* masked? */
	if ((m_command & (1<<2))==0)
	{
		state = 0;
	}

	m_rxrdy_handler(state != 0);
}



/*-------------------------------------------------
    receive_clock
-------------------------------------------------*/

void i8251_device::receive_clock()
{
	m_rxc_count++;

	if (m_rxc_count == m_br_factor)
		m_rxc_count = 0;
	else
		return;

	/* receive enable? */
	if (m_command & (1<<2))
	{
		//logerror("I8251\n");
		/* get bit received from other side and update receive register */
		receive_register_update_bit(m_rxd);

		if (is_receive_register_full())
		{
			receive_register_extract();
			receive_character(get_received_char());
		}
	}
}

/*-------------------------------------------------
    is_tx_enabled
-------------------------------------------------*/
bool i8251_device::is_tx_enabled(void) const
{
		return BIT(m_command , 0) != 0 && m_cts == 0;
}

/*-------------------------------------------------
    check_for_tx_start
-------------------------------------------------*/
void i8251_device::check_for_tx_start(void)
{
		if (is_tx_enabled() && (m_status & (I8251_STATUS_TX_EMPTY | I8251_STATUS_TX_READY)) == I8251_STATUS_TX_EMPTY) {
				start_tx();
		}
}

/*-------------------------------------------------
    start_tx
-------------------------------------------------*/
void i8251_device::start_tx(void)
{
	transmit_register_setup(m_tx_data);
	m_status &= ~I8251_STATUS_TX_EMPTY;
	m_status |= I8251_STATUS_TX_READY;
}

/*-------------------------------------------------
    transmit_clock
-------------------------------------------------*/

void i8251_device::transmit_clock()
{
	m_txc_count++;

	if (m_txc_count == m_br_factor)
		m_txc_count = 0;
	else
		return;

		if (is_transmit_register_empty()) {
				if ((m_status & I8251_STATUS_TX_READY) == 0 && (is_tx_enabled() || (m_flags & I8251_DELAYED_TX_EN) != 0)) {
						start_tx();
				} else {
						m_status |= I8251_STATUS_TX_EMPTY;
				}
				update_tx_ready();
				update_tx_empty();
		}
		/* if diserial has bits to send, make them so */
		if (!is_transmit_register_empty())
			{
				UINT8 data = transmit_register_get_data_bit();
				m_txd_handler(data);
			}

#if 0
	/* hunt mode? */
	/* after each bit has been shifted in, it is compared against the current sync byte */
	if (m_command & (1<<7))
	{
		/* data matches sync byte? */
		if (m_data == m_sync_bytes[m_sync_byte_offset])
		{
			/* sync byte matches */
			/* update for next sync byte? */
			m_sync_byte_offset++;

			/* do all sync bytes match? */
			if (m_sync_byte_offset == m_sync_byte_count)
			{
				/* ent hunt mode */
				m_command &=~(1<<7);
			}
		}
		else
		{
			/* if there is no match, reset */
			m_sync_byte_offset = 0;
		}
	}
#endif
}



/*-------------------------------------------------
    update_tx_ready
-------------------------------------------------*/

void i8251_device::update_tx_ready()
{
	/* clear tx ready state */
	int tx_ready;

	/* tx ready output is set if:
	    DB Buffer Empty &
	    CTS is set &
	    Transmit enable is 1
	*/

	tx_ready = is_tx_enabled() && (m_status & I8251_STATUS_TX_READY) != 0;

	m_txrdy_handler(tx_ready);
}



/*-------------------------------------------------
    update_tx_empty
-------------------------------------------------*/

void i8251_device::update_tx_empty()
{
	if (m_status & I8251_STATUS_TX_EMPTY)
	{
		/* tx is in marking state (high) when tx empty! */
		m_txd_handler(1);
	}

	m_txempty_handler((m_status & I8251_STATUS_TX_EMPTY) != 0);
}



//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void i8251_device::device_reset()
{
	LOG(("I8251: Reset\n"));

	/* what is the default setup when the 8251 has been reset??? */

	/* i8251 datasheet explains the state of tx pin at reset */
	/* tx is set to 1 */
	m_txd_handler(1);

	/* assumption */
	m_rts_handler(1);
	m_dtr_handler(1);

	transmit_register_reset();
	receive_register_reset();
	m_flags = 0;
	/* expecting mode byte */
	m_flags |= I8251_EXPECTING_MODE;
	/* not expecting a sync byte */
	m_flags &= ~I8251_EXPECTING_SYNC_BYTE;

	/* no character to read by cpu */
	/* transmitter is ready and is empty */
	m_status = I8251_STATUS_TX_EMPTY | I8251_STATUS_TX_READY;
	m_mode_byte = 0;
	m_command = 0;
	m_rx_data = 0;
	m_tx_data = 0;
	m_rxc_count = m_txc_count = 0;
	m_br_factor = 1;

	/* update tx empty pin output */
	update_tx_empty();
	/* update rx ready pin output */
	update_rx_ready();
	/* update tx ready pin output */
	update_tx_ready();
}



/*-------------------------------------------------
    control_w
-------------------------------------------------*/

WRITE8_MEMBER(i8251_device::command_w)
{
	/* command */
	LOG(("I8251: Command byte\n"));

	m_command = data;

	LOG(("Command byte: %02x\n", data));

	if (data & (1<<7))
	{
		LOG(("hunt mode\n"));
	}

	if (data & (1<<5))
	{
		LOG(("/rts set to 0\n"));
	}
	else
	{
		LOG(("/rts set to 1\n"));
	}

	if (data & (1<<2))
	{
		LOG(("receive enable\n"));
	}
	else
	{
		LOG(("receive disable\n"));
	}

	if (data & (1<<1))
	{
		LOG(("/dtr set to 0\n"));
	}
	else
	{
		LOG(("/dtr set to 1\n"));
	}

	if (data & (1<<0))
	{
		LOG(("transmit enable\n"));
	}
	else
	{
				LOG(("transmit disable\n"));
	}


	/*  bit 7:
	        0 = normal operation
	        1 = hunt mode
	    bit 6:
	        0 = normal operation
	        1 = internal reset
	    bit 5:
	        0 = /RTS set to 1
	        1 = /RTS set to 0
	    bit 4:
	        0 = normal operation
	        1 = reset error flag
	    bit 3:
	        0 = normal operation
	        1 = send break character
	    bit 2:
	        0 = receive disable
	        1 = receive enable
	    bit 1:
	        0 = /DTR set to 1
	        1 = /DTR set to 0
	    bit 0:
	        0 = transmit disable
	        1 = transmit enable
	*/

	m_rts_handler(!BIT(data, 5));
	m_dtr_handler(!BIT(data, 1));

	if (data & (1<<4))
	{
		m_status &= ~(I8251_STATUS_PARITY_ERROR | I8251_STATUS_OVERRUN_ERROR | I8251_STATUS_FRAMING_ERROR);
	}

	if (data & (1<<6))
	{
		// datasheet says "returns to mode format", not
		// completely resets the chip.  behavior of DEC Rainbow
		// backs this up.
		m_flags |= I8251_EXPECTING_MODE;
	}

		check_for_tx_start();
	update_rx_ready();
	update_tx_ready();
		update_tx_empty();
}

WRITE8_MEMBER(i8251_device::mode_w)
{
	LOG(("I8251: Mode byte\n"));

	m_mode_byte = data;

	/* Synchronous or Asynchronous? */
	if ((data & 0x03) != 0)
	{
		/*  Asynchronous

		    bit 7,6: stop bit length
		    0 = inhibit
		    1 = 1 bit
		    2 = 1.5 bits
		    3 = 2 bits
		    bit 5: parity type
		    0 = parity odd
		    1 = parity even
		    bit 4: parity test enable
		    0 = disable
		    1 = enable
		    bit 3,2: character length
		    0 = 5 bits
		    1 = 6 bits
		    2 = 7 bits
		    3 = 8 bits
		    bit 1,0: baud rate factor
		    0 = defines command byte for synchronous or asynchronous
		    1 = x1
		    2 = x16
		    3 = x64
		    */

		LOG(("I8251: Asynchronous operation\n"));

		LOG(("Character length: %d\n", (((data >> 2) & 0x03) + 5)));

		parity_t parity;

		if (data & (1 << 4))
		{
			LOG(("enable parity checking\n"));

			if (data & (1 << 5))
			{
				LOG(("even parity\n"));
				parity = PARITY_EVEN;
			}
			else
			{
				LOG(("odd parity\n"));
				parity = PARITY_ODD;
			}
		}
		else
		{
			LOG(("parity check disabled\n"));
			parity = PARITY_NONE;
		}

		stop_bits_t stop_bits;

		switch ((data >> 6) & 0x03)
		{
		case 0:
		default:
			stop_bits = STOP_BITS_0;
			LOG(("stop bit: inhibit\n"));
			break;

		case 1:
			stop_bits = STOP_BITS_1;
			LOG(("stop bit: 1 bit\n"));
			break;

		case 2:
			stop_bits = STOP_BITS_1_5;
			LOG(("stop bit: 1.5 bits\n"));
			break;

		case 3:
			stop_bits = STOP_BITS_2;
			LOG(("stop bit: 2 bits\n"));
			break;
		}

		int data_bits_count = ((data >> 2) & 0x03) + 5;

		set_data_frame(1, data_bits_count, parity, stop_bits);
		receive_register_reset();

		switch (data & 0x03)
		{
		case 1: m_br_factor = 1; break;
		case 2: m_br_factor = 16; break;
		case 3: m_br_factor = 64; break;
		}

		m_rxc_count = m_txc_count = 0;

#if 0
		/* data bits */
		m_receive_char_length = (((data >> 2) & 0x03) + 5);

		if (data & (1 << 4))
		{
			/* parity */
			m_receive_char_length++;
		}

		/* stop bits */
		m_receive_char_length++;

		m_receive_flags &= ~I8251_TRANSFER_RECEIVE_SYNCHRONISED;
		m_receive_flags |= I8251_TRANSFER_RECEIVE_WAITING_FOR_START_BIT;
#endif
		/* not expecting mode byte now */
		m_flags &= ~I8251_EXPECTING_MODE;
		//              m_status = I8251_STATUS_TX_EMPTY | I8251_STATUS_TX_READY;
	}
	else
	{
		/*  bit 7: Number of sync characters
		        0 = 1 character
		        1 = 2 character
		        bit 6: Synchronous mode
		        0 = Internal synchronisation
		        1 = External synchronisation
		        bit 5: parity type
		        0 = parity odd
		        1 = parity even
		        bit 4: parity test enable
		        0 = disable
		        1 = enable
		        bit 3,2: character length
		        0 = 5 bits
		        1 = 6 bits
		        2 = 7 bits
		        3 = 8 bits
		        bit 1,0 = 0
		        */
		LOG(("I8251: Synchronous operation\n"));

		/* setup for sync byte(s) */
		m_flags |= I8251_EXPECTING_SYNC_BYTE;
		m_sync_byte_offset = 0;
		if (data & 0x07)
		{
			m_sync_byte_count = 1;
		}
		else
		{
			m_sync_byte_count = 2;
		}

	}
}

WRITE8_MEMBER(i8251_device::control_w)
{
	if (m_flags & I8251_EXPECTING_MODE)
	{
		if (m_flags & I8251_EXPECTING_SYNC_BYTE)
		{
			LOG(("I8251: Sync byte\n"));

			LOG(("Sync byte: %02x\n", data));
			/* store sync byte written */
			m_sync_bytes[m_sync_byte_offset] = data;
			m_sync_byte_offset++;

			if (m_sync_byte_offset == m_sync_byte_count)
			{
				/* finished transfering sync bytes, now expecting command */
				m_flags &= ~(I8251_EXPECTING_MODE | I8251_EXPECTING_SYNC_BYTE);
				m_sync_byte_offset = 0;
			//  m_status = I8251_STATUS_TX_EMPTY | I8251_STATUS_TX_READY;
			}
		}
		else
		{
			mode_w(space, offset, data);
		}
	}
	else
	{
		command_w(space, offset, data);
	}
}



/*-------------------------------------------------
    status_r
-------------------------------------------------*/

READ8_MEMBER(i8251_device::status_r)
{
	UINT8 status = (m_dsr << 7) | m_status;

	LOG(("status: %02x\n", status));
	return status;
}



/*-------------------------------------------------
    data_w
-------------------------------------------------*/

WRITE8_MEMBER(i8251_device::data_w)
{
	m_tx_data = data;

		LOG(("data_w %02x\n" , data));

	/* writing clears */
	m_status &=~I8251_STATUS_TX_READY;
	update_tx_ready();

		// Store state of tx enable when writing to DB buffer
		if (is_tx_enabled()) {
				m_flags |= I8251_DELAYED_TX_EN;
		} else {
				m_flags &= ~I8251_DELAYED_TX_EN;
		}

		check_for_tx_start();

	/* if transmitter is active, then tx empty will be signalled */

	update_tx_ready();
	update_tx_empty();
}



/*-------------------------------------------------
    receive_character - called when last
    bit of data has been received
-------------------------------------------------*/

void i8251_device::receive_character(UINT8 ch)
{
	m_rx_data = ch;

	/* char has not been read and another has arrived! */
	if (m_status & I8251_STATUS_RX_READY)
	{
		m_status |= I8251_STATUS_OVERRUN_ERROR;
	}
	m_status |= I8251_STATUS_RX_READY;

	update_rx_ready();
}



/*-------------------------------------------------
    data_r - read data
-------------------------------------------------*/

READ8_MEMBER(i8251_device::data_r)
{
	LOG(("read data: %02x, STATUS=%02x\n",m_rx_data,m_status));
	/* reading clears */
	m_status &= ~I8251_STATUS_RX_READY;

	update_rx_ready();
	return m_rx_data;
}


void i8251_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(i8251_device::write_rxd)
{
	m_rxd = state;
//  device_serial_interface::rx_w(state);
}

WRITE_LINE_MEMBER(i8251_device::write_cts)
{
	m_cts = state;

		check_for_tx_start();
		update_tx_ready();
		update_tx_empty();
}

WRITE_LINE_MEMBER(i8251_device::write_dsr)
{
	m_dsr = !state;
}

WRITE_LINE_MEMBER(i8251_device::write_rxc)
{
	if (m_rxc != state)
	{
		m_rxc = state;

		if (m_rxc)
			receive_clock();
	}
}

WRITE_LINE_MEMBER(i8251_device::write_txc)
{
	if (m_txc != state)
	{
		m_txc = state;

		if (!m_txc)
			transmit_clock();
	}
}