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

                               

                                                                            
                                                         
                                                                         

                                                              
 




                                                                                     
 





                                                                            
                      



                                    

                                    


  
                
                    
 


                                
 

                         
 
                                                                            
                      
                                                                            
 
                         








                                                                                           
 
 
                                                   
                                                 

                                                   
                                                        



                                                      




                                                                            
                                                   
                                
                                                   
 
























                                                                                                                                                   
 

                                       

 
                                                                                                               
                                                                        
 
 
 
                                                                                                               
                                                                        


 
                                                                                                               
                                                                        


 
                                                                                                               
                                                                        


 
                                                                                                               
                                                                        


 
                                                                                                               
                                                                        


 
                                                                                                           
                                                                       


 
                                                                                                               
                                                                           


 
 
                                                   



                                                   
 
                            














                                       
 
                              

                                 
 
                              

                                 
 
                       
                                     

 
 


                                                   
 

                                   
                                             
 
 




                                                   
 

                      
 
                                                                              
                                                                                               
                                                                                                












                                                                               
                                                                   

                     

 
 




                                                   
 
              
 
                                                             
 

                                          
         




                                                                                        
                                                         

                                           
                                                                                                                  
 
                                                     
                 

         
                                                                             
 
                                     
 
 
 







                                                   
                                                             













                                                                  
                                                                                       

 
 
                                                   
                      




                                                                                      
                            



                                                   




                                                   
                                       
         
                         







                                                   

                                                   
                                                            
 
                                          
                     
 















                                                                    
 
                                           






                                                                                              
                                                 












                                                                                            
         
                                                
                                                 



                                                                                          

         
                                                                                             
 
                                 
                                    

                            
                                               
                                         





                              









                                                   





























































                                                                   








                                                                            
                                                                                                                 















                                                               
 

                                
 

                                






                                                   
                                    
 


                                                     


                              

                                             
                                    
                                    





                                        
                                    


                                



                                                   
                                

                                                   
                                    
 


                                  

                                  
 

                                  
                                       
                                

                               

                   
 

                                                 
         
                                           
         
 
 
 
                                                   
                  

                                                   
                                    
 

                                       
                                



                                                         



                                          



                                                         



                                               



                                                                                
         








                                                   
                                                                                                         
         
                                                                                                                         
 
                                                   
 








                                                                         



                                                         


            
                                



                                                         






















                                                                                                     
                                                   















                                                   


                                                   
                                     


                       
                                              
         



                                                          




                      
 



                                                   
                                                                     
 
                                             
         


                                                     

         
                           

 
 



                                                   
                                         


                     
                                                  
         



                                                          




                    
 



                                                   
                                         


                     
                                                  
         



                                                      




                    
 
                                                   


                                                   
                                       
 
                         
 
                                              
 





                                              



                    
                                 


                      
                                 
                                                         
                                         


                      
                                                                                                                 



                    
 


                                                   
 
                                                 
 
                                              
                                 
 
                                                                                                                
 
                                          

                                 


                     
                                          
                                              




                    
                                                
                 
                              
                                                                                                 


                                    

                                                                                                                                 


                                          
                                                          
                                                                                   
 


                                                       
 
                                           
 
                                                                                                                            

                              
                                       
                                        
                                                                                                          
                                       

                              
                                            
                                                                     
                                                                                                                                        
                                       

                              
                                      
                                                              

                                                                                                                                                          

                              
                                     
                                      
                                                                                                        
                                                                                                      

                              
                                         
                                                
                                                                                                                  
                                                    

                                                                                                                                    
                                                                         






                                                                                                                    




                              





                                                                                                                                                          
 
                                                    
                 
                                        
                                                                                                                        

                              
                                      
                                                                                                                                  

                              
                                           
                                                                                                                                                        

                              
                                    
                                                                                                                                 


                              
                                           


                      
                                   
                                                         
                 
                                               



                                                                                 
                                           
                                                                                                                


                      


                                                                                                                                       
 

                                        


                      



                                                                                                                                         
 

                                        


                      




                                                                                                                                          
 

                                        
 
                                   
                 
                                                                            
                                   
                                  


                    

                                                                                                             

                 
                                                                                             
                                                  
                      

               
                                                                                                             



                                                  
                                                                                                            

                                                       


         
 



                                                   
                                    
 
                         
 
                                    
         
                                          
                                                
 
                                                  
                                                                                                                                     
 
                                           
                 
                                                                   
                                                           


                 
                                                                                                            



                    
 


                                                   
 
                                              

                         
 

                                                                      
                                                                                                                         
 












                                                                         
                                 
 
                                                                                                             

 
 


                                                   
 
                                                
 
                                                                                                           
 
                                  
         
                                                 
                                                   
 
                                                       
                 
                                      
                                        
                         
                                                                                


                              

                                           
                                                                        

                              


                                                                                                    


            


                                                                      

         
                                         
 
                            
                                               
         
                              
                               
                 
                                                                        
 
                                       


                      

                                   
                                                                



                      
 


                                                   
 
                                           
 
                                                                                          
 
                           
         
                                                             
                           

                                                         
 

                                    
 
                                    
                 
                                   
                                                   
                            
                                                    
 
                                            
                                                         
                         
                                                    
                                                                                 
 

                                                        




                         
 


                                                   
 
                                           
 
                                                                                          
 
                           
         
                                                          
                           

                                                         
 

                                          
 
                                    
                 
                                  
                                                   
                            
                                                    
 
                                                         
                         
                                                    
                                                                                 
 

                                                        




                         
 


                                                   
 
                                          
 
                                                                                         
 
                          
         

                                           
 
                                    
                 
                                 
                                                  
                            
                                                   
 
                                                         
                         
                                                    
                                                                                 
 

                                                        




                         
 
                                                   


                                                   
                                            
 
                                                                                           



                                                   
                         
                                                   
 
                                           
 
                                                                                                   



                                      
         




                                                  
 


         
 
                                                   
                          
                                                   
 
                                           
 
                                                                                                      



                                      
         




                                                  
 


         
 





                                                   

                                                  
 
                        


                                              
                                             
                    
                                            
         

                                     
 
                                                             
 

                                      








                                             
                                                                                                     










                                                   



                                                 








                                                   



                                                 
 







                                                   
                      


                                                     
 
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

    Intel 8274 Multi-Protocol Serial Controller emulation
    NEC µPD7201 Multiprotocol Serial Communications Controller emulation
    Z80-DART Dual Asynchronous Receiver/Transmitter emulation
    Z80-SIO/0/1/2/3/4 Serial Input/Output Controller emulation

    The z80dart/z80sio itself is based on an older intel serial chip, the i8274 MPSC
    (see http://doc.chipfind.ru/pdf/intel/8274.pdf), which also has almost identical
    behavior, except lacks the interrupt daisy chaining and has its own interrupt/dma
    scheme which uses write register 2 on channel A, that register which is unused on
    the z80dart and z80sio.

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

/*

    TODO:

    - i8274 DMA scheme
    - break detection
    - wr0 reset tx interrupt pending
    - wait/ready
    - 1.5 stop bits
    - synchronous mode (Z80-SIO/1,2)
    - SDLC mode (Z80-SIO/1,2)

*/

#include "emu.h"
#include "z80dart.h"

//#define VERBOSE 1
//#define LOG_OUTPUT_FUNC printf
#include "logmacro.h"

#define CHANA_TAG   "cha"
#define CHANB_TAG   "chb"

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

// device type definition
DEFINE_DEVICE_TYPE(Z80DART,         z80dart_device,  "z80dart",         "Z80 DART")
DEFINE_DEVICE_TYPE(Z80DART_CHANNEL, z80dart_channel, "z80dart_channel", "Z80 DART channel")
DEFINE_DEVICE_TYPE(Z80SIO0,         z80sio0_device,  "z80sio0",         "Z80 SIO/0")
DEFINE_DEVICE_TYPE(Z80SIO1,         z80sio1_device,  "z80sio1",         "Z80 SIO/1")
DEFINE_DEVICE_TYPE(Z80SIO2,         z80sio2_device,  "z80sio2",         "Z80 SIO/2")
DEFINE_DEVICE_TYPE(Z80SIO3,         z80sio3_device,  "z80sio3",         "Z80 SIO/3")
DEFINE_DEVICE_TYPE(Z80SIO4,         z80sio4_device,  "z80sio4",         "Z80 SIO/4")
DEFINE_DEVICE_TYPE(I8274,           i8274_device,    "i8274",           "Intel 8274 MPSC")
DEFINE_DEVICE_TYPE(UPD7201,         upd7201_device,  "upd7201",         "NEC uPD7201 MPSC")


//-------------------------------------------------
//  device_add_mconfig - add device configuration
//-------------------------------------------------

MACHINE_CONFIG_START(z80dart_device::device_add_mconfig)
	MCFG_DEVICE_ADD(CHANA_TAG, Z80DART_CHANNEL, 0)
	MCFG_DEVICE_ADD(CHANB_TAG, Z80DART_CHANNEL, 0)
MACHINE_CONFIG_END


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

//-------------------------------------------------
//  z80dart_device - constructor
//-------------------------------------------------

z80dart_device::z80dart_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t variant)
	: device_t(mconfig, type, tag, owner, clock)
	, device_z80daisy_interface(mconfig, *this)
	, m_chanA(*this, CHANA_TAG)
	, m_chanB(*this, CHANB_TAG)
	, m_rxca(0)
	, m_txca(0)
	, m_rxcb(0)
	, m_txcb(0)
	, m_out_txda_cb(*this)
	, m_out_dtra_cb(*this)
	, m_out_rtsa_cb(*this)
	, m_out_wrdya_cb(*this)
	, m_out_synca_cb(*this)
	, m_out_txdb_cb(*this)
	, m_out_dtrb_cb(*this)
	, m_out_rtsb_cb(*this)
	, m_out_wrdyb_cb(*this)
	, m_out_syncb_cb(*this)
	, m_out_int_cb(*this)
	, m_out_rxdrqa_cb(*this)
	, m_out_txdrqa_cb(*this)
	, m_out_rxdrqb_cb(*this)
	, m_out_txdrqb_cb(*this)
	, m_variant(variant)
{
	for (auto & elem : m_int_state)
		elem = 0;
}

z80dart_device::z80dart_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: z80dart_device(mconfig, Z80DART, tag, owner, clock, TYPE_DART)
{
}

z80sio0_device::z80sio0_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: z80dart_device(mconfig, Z80SIO0, tag, owner, clock, TYPE_SIO0)
{
}

z80sio1_device::z80sio1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: z80dart_device(mconfig, Z80SIO1, tag, owner, clock, TYPE_SIO1)
{
}

z80sio2_device::z80sio2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: z80dart_device(mconfig, Z80SIO2, tag, owner, clock, TYPE_SIO2)
{
}

z80sio3_device::z80sio3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: z80dart_device(mconfig, Z80SIO3, tag, owner, clock, TYPE_SIO3)
{
}

z80sio4_device::z80sio4_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: z80dart_device(mconfig, Z80SIO4, tag, owner, clock, TYPE_SIO4)
{
}

i8274_device::i8274_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: z80dart_device(mconfig, I8274, tag, owner, clock, TYPE_I8274)
{
}

upd7201_device::upd7201_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: z80dart_device(mconfig, UPD7201, tag, owner, clock, TYPE_UPD7201)
{
}


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

void z80dart_device::device_start()
{
	// resolve callbacks
	m_out_txda_cb.resolve_safe();
	m_out_dtra_cb.resolve_safe();
	m_out_rtsa_cb.resolve_safe();
	m_out_wrdya_cb.resolve_safe();
	m_out_synca_cb.resolve_safe();
	m_out_txdb_cb.resolve_safe();
	m_out_dtrb_cb.resolve_safe();
	m_out_rtsb_cb.resolve_safe();
	m_out_wrdyb_cb.resolve_safe();
	m_out_syncb_cb.resolve_safe();
	m_out_int_cb.resolve_safe();
	m_out_rxdrqa_cb.resolve_safe();
	m_out_txdrqa_cb.resolve_safe();
	m_out_rxdrqb_cb.resolve_safe();
	m_out_txdrqb_cb.resolve_safe();

	// configure channel A
	m_chanA->set_rxc(m_rxca);
	m_chanA->set_txc(m_txca);

	// configure channel B
	m_chanB->set_rxc(m_rxcb);
	m_chanB->set_txc(m_txcb);

	// state saving
	save_item(NAME(m_int_state));
}


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

void z80dart_device::device_reset()
{
	LOG("Z80DART \"%s\" Reset\n", tag());
}

//-------------------------------------------------
//  z80daisy_irq_state - get interrupt status
//-------------------------------------------------

int z80dart_device::z80daisy_irq_state()
{
	int state = 0;
	int i;

	LOG("Z80DART \"%s\" : Interrupt State A:%d%d%d%d B:%d%d%d%d\n", tag(),
				m_int_state[0], m_int_state[1], m_int_state[2], m_int_state[3],
				m_int_state[4], m_int_state[5], m_int_state[6], m_int_state[7]);

	// loop over all interrupt sources
	for (i = 0; i < 8; i++)
	{
		// if we're servicing a request, don't indicate more interrupts
		if (m_int_state[i] & Z80_DAISY_IEO)
		{
			state |= Z80_DAISY_IEO;
			break;
		}
		state |= m_int_state[i];
	}

	LOG("Z80DART \"%s\" : Interrupt State %u\n", tag(), state);

	return state;
}


//-------------------------------------------------
//  z80daisy_irq_ack - interrupt acknowledge
//-------------------------------------------------

int z80dart_device::z80daisy_irq_ack()
{
	int i;

	LOG("Z80DART \"%s\" Interrupt Acknowledge\n", tag());

	// loop over all interrupt sources
	for (i = 0; i < 8; i++)
	{
		// find the first channel with an interrupt requested
		if (m_int_state[i] & Z80_DAISY_INT)
		{
			// clear interrupt, switch to the IEO state, and update the IRQs
			m_int_state[i] = Z80_DAISY_IEO;
			m_chanA->clr_interrupt_pending();
			check_interrupts();

			LOG("Z80DART \"%s\" : Interrupt Acknowledge Vector %02x\n", tag(), m_chanB->get_vector());

			return m_chanB->get_vector();
		}
	}

	//logerror("z80dart_irq_ack: failed to find an interrupt to ack!\n");

	return m_chanB->get_vector();
}


//-------------------------------------------------
//  z80daisy_irq_reti - return from interrupt
//-------------------------------------------------

void z80dart_device::z80daisy_irq_reti()
{
	int i;

	LOG("Z80DART \"%s\" Return from Interrupt\n", tag());

	// loop over all interrupt sources
	for (i = 0; i < 8; i++)
	{
		// find the first channel with an IEO pending
		if (m_int_state[i] & Z80_DAISY_IEO)
		{
			// clear the IEO state and update the IRQs
			m_int_state[i] &= ~Z80_DAISY_IEO;
			check_interrupts();
			return;
		}
	}

	//logerror("z80dart_irq_reti: failed to find an interrupt to clear IEO on!\n");
}


//-------------------------------------------------
//  check_interrupts -
//-------------------------------------------------

void z80dart_device::check_interrupts()
{
	int state = (z80daisy_irq_state() & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE;
	m_out_int_cb(state);
}


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

void z80dart_device::reset_interrupts()
{
	for (auto & elem : m_int_state)
	{
		elem = 0;
	}

	check_interrupts();
}


//-------------------------------------------------
//  trigger_interrupt -
//-------------------------------------------------

void z80dart_device::trigger_interrupt(int index, int state)
{
	uint8_t vector = m_chanB->m_wr[2];
	int priority;

	if((m_variant == TYPE_I8274) || (m_variant == TYPE_UPD7201))
	{
		int prio_level = 0;
		switch(state)
		{
			case z80dart_channel::INT_TRANSMIT:
				prio_level = 1;
				break;
			case z80dart_channel::INT_RECEIVE:
			case z80dart_channel::INT_SPECIAL:
				prio_level = 0;
				break;
			case z80dart_channel::INT_EXTERNAL:
				prio_level = 2;
				break;
		}

		if(m_chanA->get_priority())
		{
			priority = (prio_level * 2) + index;
		}
		else
		{
			priority = (prio_level == 2) ? index + 4 : ((index * 2) + prio_level);
		}
		if (m_chanB->get_status_vector())
		{
			vector = (!index << 2) | state;
			if((m_chanA->m_wr[1] & 0x18) == z80dart_channel::WR2_MODE_8086_8088)
			{
				vector = (m_chanB->m_wr[2] & 0xf8) | vector;
			}
			else
			{
				vector = (m_chanB->m_wr[2] & 0xe3) | (vector << 2);
			}
		}
	}
	else
	{
		priority = (index << 2) | state;
		if (m_chanB->get_status_vector())
		{
			// status affects vector
			vector = (m_chanB->m_wr[2] & 0xf1) | (!index << 3) | (state << 1);
		}
	}

	LOG("Z80DART \"%s\" Channel %c : Interrupt Request %u\n", tag(), 'A' + index, state);

	// update vector register
	m_chanB->set_vector(vector);

	// trigger interrupt
	m_int_state[priority] |= Z80_DAISY_INT;
	m_chanA->set_interrupt_pending();

	// check for interrupt
	check_interrupts();
}


//-------------------------------------------------
//  m1_r - interrupt acknowledge
//-------------------------------------------------

int z80dart_device::m1_r()
{
	return z80daisy_irq_ack();
}


//-------------------------------------------------
//  cd_ba_r -
//-------------------------------------------------

READ8_MEMBER( z80dart_device::cd_ba_r )
{
	int ba = BIT(offset, 0);
	int cd = BIT(offset, 1);
	z80dart_channel *channel = ba ? m_chanB : m_chanA;

	return cd ? channel->control_read() : channel->data_read();
}


//-------------------------------------------------
//  cd_ba_w -
//-------------------------------------------------

WRITE8_MEMBER( z80dart_device::cd_ba_w )
{
	int ba = BIT(offset, 0);
	int cd = BIT(offset, 1);
	z80dart_channel *channel = ba ? m_chanB : m_chanA;

	if (cd)
		channel->control_write(data);
	else
		channel->data_write(data);
}


//-------------------------------------------------
//  ba_cd_r -
//-------------------------------------------------

READ8_MEMBER( z80dart_device::ba_cd_r )
{
	int ba = BIT(offset, 1);
	int cd = BIT(offset, 0);
	z80dart_channel *channel = ba ? m_chanB : m_chanA;

	return cd ? channel->control_read() : channel->data_read();
}


//-------------------------------------------------
//  ba_cd_w -
//-------------------------------------------------

WRITE8_MEMBER( z80dart_device::ba_cd_w )
{
	int ba = BIT(offset, 1);
	int cd = BIT(offset, 0);
	z80dart_channel *channel = ba ? m_chanB : m_chanA;

	if (cd)
		channel->control_write(data);
	else
		channel->data_write(data);
}



//**************************************************************************
//  DART CHANNEL
//**************************************************************************

//-------------------------------------------------
//  dart_channel - constructor
//-------------------------------------------------

z80dart_channel::z80dart_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, Z80DART_CHANNEL, tag, owner, clock)
	, device_serial_interface(mconfig, *this)
	, m_rx_error(0)
	, m_rx_clock(0)
	, m_rx_first(0)
	, m_rx_break(0)
	, m_rx_rr0_latch(0)
	, m_rxd(0)
	, m_ri(0)
	, m_cts(0)
	, m_dcd(0)
	, m_tx_data(0)
	, m_tx_clock(0)
	, m_dtr(0)
	, m_rts(0)
	, m_sync(0)
{
	for (auto & elem : m_rr)
		elem = 0;

	for (auto & elem : m_wr)
		elem = 0;
}


//-------------------------------------------------
//  start - channel startup
//-------------------------------------------------

void z80dart_channel::device_start()
{
	m_uart = downcast<z80dart_device *>(owner());
	m_index = m_uart->get_channel_index(this);

	// state saving
	save_item(NAME(m_rr));
	save_item(NAME(m_wr));
	//  save_item(NAME(m_rx_data_fifo));
	//  save_item(NAME(m_rx_error_fifo));
	save_item(NAME(m_rx_clock));
	save_item(NAME(m_rx_first));
	save_item(NAME(m_rx_break));
	save_item(NAME(m_rx_rr0_latch));
	save_item(NAME(m_ri));
	save_item(NAME(m_cts));
	save_item(NAME(m_dcd));
	save_item(NAME(m_tx_data));
	save_item(NAME(m_tx_clock));
	save_item(NAME(m_dtr));
	save_item(NAME(m_rts));
	save_item(NAME(m_sync));
}


//-------------------------------------------------
//  reset - reset channel status
//-------------------------------------------------

void z80dart_channel::device_reset()
{
	receive_register_reset();
	transmit_register_reset();

	// disable receiver
	m_wr[3] &= ~WR3_RX_ENABLE;

	// disable transmitter
	m_wr[5] &= ~WR5_TX_ENABLE;
	m_rr[0] |= RR0_TX_BUFFER_EMPTY;
	m_rr[1] |= RR1_ALL_SENT;

	// reset external lines
	set_rts(1);
	set_dtr(1);

	// reset interrupts
	if (m_index == z80dart_device::CHANNEL_A)
	{
		m_uart->reset_interrupts();
	}
}


//-------------------------------------------------
//  tra_callback -
//-------------------------------------------------

void z80dart_channel::tra_callback()
{
	if (!(m_wr[5] & WR5_TX_ENABLE))
	{
		// transmit mark
		if (m_index == z80dart_device::CHANNEL_A)
			m_uart->m_out_txda_cb(1);
		else
			m_uart->m_out_txdb_cb(1);
	}
	else if (m_wr[5] & WR5_SEND_BREAK)
	{
		// transmit break
		if (m_index == z80dart_device::CHANNEL_A)
			m_uart->m_out_txda_cb(0);
		else
			m_uart->m_out_txdb_cb(0);
	}
	else if (!is_transmit_register_empty())
	{
		// transmit data
		if (m_index == z80dart_device::CHANNEL_A)
			m_uart->m_out_txda_cb(transmit_register_get_data_bit());
		else
			m_uart->m_out_txdb_cb(transmit_register_get_data_bit());
	}
}


//-------------------------------------------------
//  tra_complete -
//-------------------------------------------------

void z80dart_channel::tra_complete()
{
	if ((m_wr[5] & WR5_TX_ENABLE) && !(m_wr[5] & WR5_SEND_BREAK) && !(m_rr[0] & RR0_TX_BUFFER_EMPTY))
	{
		LOG("Z80DART \"%s\" Channel %c : Transmit Data Byte '%02x'\n", owner()->tag(), 'A' + m_index, m_tx_data);

		transmit_register_setup(m_tx_data);

		// empty transmit buffer
		m_rr[0] |= RR0_TX_BUFFER_EMPTY;

		if (m_wr[1] & WR1_TX_INT_ENABLE)
			m_uart->trigger_interrupt(m_index, INT_TRANSMIT);
	}
	else if (m_wr[5] & WR5_SEND_BREAK)
	{
		// transmit break
		if (m_index == z80dart_device::CHANNEL_A)
			m_uart->m_out_txda_cb(0);
		else
			m_uart->m_out_txdb_cb(0);
	}
	else
	{
		// transmit mark
		if (m_index == z80dart_device::CHANNEL_A)
			m_uart->m_out_txda_cb(1);
		else
			m_uart->m_out_txdb_cb(1);
	}

	// if transmit buffer is empty
	if (m_rr[0] & RR0_TX_BUFFER_EMPTY)
	{
		// then all characters have been sent
		m_rr[1] |= RR1_ALL_SENT;

		// when the RTS bit is reset, the _RTS output goes high after the transmitter empties
		if (!m_rts)
			set_rts(1);
	}
}


//-------------------------------------------------
//  rcv_callback -
//-------------------------------------------------

void z80dart_channel::rcv_callback()
{
	if (m_wr[3] & WR3_RX_ENABLE)
	{
		receive_register_update_bit(m_rxd);
	}
}


//-------------------------------------------------
//  rcv_complete -
//-------------------------------------------------

void z80dart_channel::rcv_complete()
{
	receive_register_extract();
	receive_data(get_received_char());
}


//-------------------------------------------------
//  get_clock_mode - get clock divisor
//-------------------------------------------------

int z80dart_channel::get_clock_mode()
{
	int clocks = 1;

	switch (m_wr[4] & WR4_CLOCK_RATE_MASK)
	{
	case WR4_CLOCK_RATE_X1:     clocks = 1;     break;
	case WR4_CLOCK_RATE_X16:    clocks = 16;    break;
	case WR4_CLOCK_RATE_X32:    clocks = 32;    break;
	case WR4_CLOCK_RATE_X64:    clocks = 64;    break;
	}

	return clocks;
}


//-------------------------------------------------
//  get_stop_bits - get number of stop bits
//-------------------------------------------------

device_serial_interface::stop_bits_t z80dart_channel::get_stop_bits()
{
	switch (m_wr[4] & WR4_STOP_BITS_MASK)
	{
	case WR4_STOP_BITS_1: return STOP_BITS_1;
	case WR4_STOP_BITS_1_5: return STOP_BITS_1_5;
	case WR4_STOP_BITS_2: return STOP_BITS_2;
	}

	return STOP_BITS_0;
}


//-------------------------------------------------
//  get_rx_word_length - get receive word length
//-------------------------------------------------

int z80dart_channel::get_rx_word_length()
{
	int bits = 5;

	switch (m_wr[3] & WR3_RX_WORD_LENGTH_MASK)
	{
	case WR3_RX_WORD_LENGTH_5:  bits = 5;       break;
	case WR3_RX_WORD_LENGTH_6:  bits = 6;       break;
	case WR3_RX_WORD_LENGTH_7:  bits = 7;       break;
	case WR3_RX_WORD_LENGTH_8:  bits = 8;       break;
	}

	return bits;
}


//-------------------------------------------------
//  get_tx_word_length - get transmit word length
//-------------------------------------------------

int z80dart_channel::get_tx_word_length()
{
	int bits = 5;

	switch (m_wr[5] & WR5_TX_WORD_LENGTH_MASK)
	{
	case WR5_TX_WORD_LENGTH_5:  bits = 5;   break;
	case WR5_TX_WORD_LENGTH_6:  bits = 6;   break;
	case WR5_TX_WORD_LENGTH_7:  bits = 7;   break;
	case WR5_TX_WORD_LENGTH_8:  bits = 8;   break;
	}

	return bits;
}


//-------------------------------------------------
//  control_read - read control register
//-------------------------------------------------

uint8_t z80dart_channel::control_read()
{
	uint8_t data = 0;

	int reg = m_wr[0] & WR0_REGISTER_MASK;

	if (reg != 0)
	{
		// mask out register index
		m_wr[0] &= ~WR0_REGISTER_MASK;
	}

	switch (reg)
	{
	case 0:
	case 1:
		data = m_rr[reg];
		break;

	case 2:
		// channel B only
		if (m_index == z80dart_device::CHANNEL_B)
			data = m_rr[reg];
		break;
	}

	//LOG("Z80DART \"%s\" Channel %c : Control Register Read '%02x'\n", owner()->tag(), 'A' + m_index, data);

	return data;
}


//-------------------------------------------------
//  control_write - write control register
//-------------------------------------------------

void z80dart_channel::control_write(uint8_t data)
{
	int reg = m_wr[0] & WR0_REGISTER_MASK;
	uint8_t prev = m_wr[reg];

	LOG("Z80DART \"%s\" Channel %c : Control Register Write '%02x'\n", owner()->tag(), 'A' + m_index, data);

	// write data to selected register
	if (reg < 6)
		m_wr[reg] = data;

	if (reg != 0)
	{
		// mask out register index
		m_wr[0] &= ~WR0_REGISTER_MASK;
	}

	switch (reg)
	{
	case 0:
		switch (data & WR0_COMMAND_MASK)
		{
		case WR0_NULL:
			LOG("Z80DART \"%s\" Channel %c : Null\n", owner()->tag(), 'A' + m_index);
			break;

		case WR0_SEND_ABORT:
			LOG("Z80DART \"%s\" Channel %c : Send Abort\n", owner()->tag(), 'A' + m_index);
			logerror("Z80DART \"%s\" Channel %c : unsupported command: Send Abort\n", owner()->tag(), 'A' + m_index);
			break;

		case WR0_RESET_EXT_STATUS:
			// reset external/status interrupt
			m_rr[0] &= ~(RR0_DCD | RR0_RI | RR0_CTS | RR0_BREAK_ABORT);

			if (!m_dcd) m_rr[0] |= RR0_DCD;
			if (m_ri) m_rr[0] |= RR0_RI;
			if (m_cts) m_rr[0] |= RR0_CTS;

			m_rx_rr0_latch = 0;

			LOG("Z80DART \"%s\" Channel %c : Reset External/Status Interrupt\n", owner()->tag(), 'A' + m_index);
			break;

		case WR0_CHANNEL_RESET:
			// channel reset
			LOG("Z80DART \"%s\" Channel %c : Channel Reset\n", owner()->tag(), 'A' + m_index);
			device_reset();
			break;

		case WR0_ENABLE_INT_NEXT_RX:
			// enable interrupt on next receive character
			LOG("Z80DART \"%s\" Channel %c : Enable Interrupt on Next Received Character\n", owner()->tag(), 'A' + m_index);
			m_rx_first = 1;
			break;

		case WR0_RESET_TX_INT:
			// reset transmitter interrupt pending
			LOG("Z80DART \"%s\" Channel %c : Reset Transmitter Interrupt Pending\n", owner()->tag(), 'A' + m_index);
			logerror("Z80DART \"%s\" Channel %c : unsupported command: Reset Transmitter Interrupt Pending\n", owner()->tag(), 'A' + m_index);
			break;

		case WR0_ERROR_RESET:
			// error reset
			LOG("Z80DART \"%s\" Channel %c : Error Reset\n", owner()->tag(), 'A' + m_index);
			m_rr[1] &= ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR);
			break;

		case WR0_RETURN_FROM_INT:
			// return from interrupt
			LOG("Z80DART \"%s\" Channel %c : Return from Interrupt\n", owner()->tag(), 'A' + m_index);
			m_uart->z80daisy_irq_reti();
			if((m_uart->m_variant == z80dart_device::TYPE_I8274) || (m_uart->m_variant == z80dart_device::TYPE_UPD7201))
			{
				if (m_uart->m_chanB->get_status_vector())
				{
					if((m_uart->m_chanA->m_wr[1] & 0x18) == z80dart_channel::WR2_MODE_8086_8088)
						m_uart->m_chanB->m_rr[2] = (m_uart->m_chanB->m_wr[2] & 0xf8) | 0x07;
					else
						m_uart->m_chanB->m_rr[2] = (m_uart->m_chanB->m_wr[2] & 0xe3) | 0x1c;
				}
			}
			break;
		}
		break;

	case 1:
		LOG("Z80DART \"%s\" Channel %c : External Interrupt Enable %u\n", owner()->tag(), 'A' + m_index, (data & WR1_EXT_INT_ENABLE) ? 1 : 0);
		LOG("Z80DART \"%s\" Channel %c : Transmit Interrupt Enable %u\n", owner()->tag(), 'A' + m_index, (data & WR1_TX_INT_ENABLE) ? 1 : 0);
		LOG("Z80DART \"%s\" Channel %c : Status Affects Vector %u\n", owner()->tag(), 'A' + m_index, (data & WR1_STATUS_VECTOR) ? 1 : 0);
		LOG("Z80DART \"%s\" Channel %c : Wait/Ready Enable %u\n", owner()->tag(), 'A' + m_index, (data & WR1_WRDY_ENABLE) ? 1 : 0);
		LOG("Z80DART \"%s\" Channel %c : Wait/Ready Function %s\n", owner()->tag(), 'A' + m_index, (data & WR1_WRDY_FUNCTION) ? "Ready" : "Wait");
		LOG("Z80DART \"%s\" Channel %c : Wait/Ready on %s\n", owner()->tag(), 'A' + m_index, (data & WR1_WRDY_ON_RX_TX) ? "Receive" : "Transmit");

		switch (data & WR1_RX_INT_MODE_MASK)
		{
		case WR1_RX_INT_DISABLE:
			LOG("Z80DART \"%s\" Channel %c : Receiver Interrupt Disabled\n", owner()->tag(), 'A' + m_index);
			break;

		case WR1_RX_INT_FIRST:
			LOG("Z80DART \"%s\" Channel %c : Receiver Interrupt on First Character\n", owner()->tag(), 'A' + m_index);
			break;

		case WR1_RX_INT_ALL_PARITY:
			LOG("Z80DART \"%s\" Channel %c : Receiver Interrupt on All Characters, Parity Affects Vector\n", owner()->tag(), 'A' + m_index);
			break;

		case WR1_RX_INT_ALL:
			LOG("Z80DART \"%s\" Channel %c : Receiver Interrupt on All Characters\n", owner()->tag(), 'A' + m_index);
			break;
		}

		m_uart->check_interrupts();
		break;

	case 2:
		// interrupt vector
		if (m_index == z80dart_device::CHANNEL_B)
		{
			if(get_status_vector())
				m_rr[2] = ( m_rr[2] & 0x0e ) | ( m_wr[2] & 0xF1);
			else
				m_rr[2] = m_wr[2];
		}
		m_uart->check_interrupts();
		LOG("Z80DART \"%s\" Channel %c : Interrupt Vector %02x\n", owner()->tag(), 'A' + m_index, data);
		break;

	case 3:
		LOG("Z80DART \"%s\" Channel %c : Receiver Enable %u\n", owner()->tag(), 'A' + m_index, (data & WR3_RX_ENABLE) ? 1 : 0);
		LOG("Z80DART \"%s\" Channel %c : Auto Enables %u\n", owner()->tag(), 'A' + m_index, (data & WR3_AUTO_ENABLES) ? 1 : 0);
		LOG("Z80DART \"%s\" Channel %c : Receiver Bits/Character %u\n", owner()->tag(), 'A' + m_index, get_rx_word_length());

		if (data != prev)
			update_serial();
		break;

	case 4:
		LOG("Z80DART \"%s\" Channel %c : Parity Enable %u\n", owner()->tag(), 'A' + m_index, (data & WR4_PARITY_ENABLE) ? 1 : 0);
		LOG("Z80DART \"%s\" Channel %c : Parity %s\n", owner()->tag(), 'A' + m_index, (data & WR4_PARITY_EVEN) ? "Even" : "Odd");
		LOG("Z80DART \"%s\" Channel %c : Stop Bits %s\n", owner()->tag(), 'A' + m_index, stop_bits_tostring(get_stop_bits()));
		LOG("Z80DART \"%s\" Channel %c : Clock Mode %uX\n", owner()->tag(), 'A' + m_index, get_clock_mode());

		if (data != prev)
			update_serial();
		break;

	case 5:
		LOG("Z80DART \"%s\" Channel %c : Transmitter Enable %u\n", owner()->tag(), 'A' + m_index, (data & WR5_TX_ENABLE) ? 1 : 0);
		LOG("Z80DART \"%s\" Channel %c : Transmitter Bits/Character %u\n", owner()->tag(), 'A' + m_index, get_tx_word_length());
		LOG("Z80DART \"%s\" Channel %c : Send Break %u\n", owner()->tag(), 'A' + m_index, (data & WR5_SEND_BREAK) ? 1 : 0);
		LOG("Z80DART \"%s\" Channel %c : Request to Send %u\n", owner()->tag(), 'A' + m_index, (data & WR5_RTS) ? 1 : 0);
		LOG("Z80DART \"%s\" Channel %c : Data Terminal Ready %u\n", owner()->tag(), 'A' + m_index, (data & WR5_DTR) ? 1 : 0);

		if (data != prev)
			update_serial();

		if (data & WR5_RTS)
		{
			// when the RTS bit is set, the _RTS output goes low
			set_rts(0);
			m_rts = 1;
		}
		else
		{
			// when the RTS bit is reset, the _RTS output goes high after the transmitter empties
			m_rts = 0;
		}

		// data terminal ready output follows the state programmed into the DTR bit*/
		set_dtr((data & WR5_DTR) ? 0 : 1);
		break;

	case 6:
		LOG("Z80DART \"%s\" Channel %c : Transmit Sync %02x\n", owner()->tag(), 'A' + m_index, data);
		m_sync = (m_sync & 0xff00) | data;
		break;

	case 7:
		LOG("Z80DART \"%s\" Channel %c : Receive Sync %02x\n", owner()->tag(), 'A' + m_index, data);
		m_sync = (data << 8) | (m_sync & 0xff);
		break;
	}
}


//-------------------------------------------------
//  data_read - read data register
//-------------------------------------------------

uint8_t z80dart_channel::data_read()
{
	uint8_t data = 0;

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

		// load error status from the FIFO
		m_rr[1] = (m_rr[1] & ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR)) | m_rx_error_fifo.dequeue();

		if (m_rx_data_fifo.empty())
		{
			// no more characters available in the FIFO
			m_rr[0] &= ~ RR0_RX_CHAR_AVAILABLE;
		}
	}

	LOG("Z80DART \"%s\" Channel %c : Data Register Read '%02x'\n", owner()->tag(), 'A' + m_index, data);

	return data;
}


//-------------------------------------------------
//  data_write - write data register
//-------------------------------------------------

void z80dart_channel::data_write(uint8_t data)
{
	m_tx_data = data;

	if ((m_wr[5] & WR5_TX_ENABLE) && is_transmit_register_empty())
	{
		LOG("Z80DART \"%s\" Channel %c : Transmit Data Byte '%02x'\n", owner()->tag(), 'A' + m_index, m_tx_data);

		transmit_register_setup(m_tx_data);

		// empty transmit buffer
		m_rr[0] |= RR0_TX_BUFFER_EMPTY;

		if (m_wr[1] & WR1_TX_INT_ENABLE)
			m_uart->trigger_interrupt(m_index, INT_TRANSMIT);
	}
	else
	{
		m_rr[0] &= ~RR0_TX_BUFFER_EMPTY;
	}

	m_rr[1] &= ~RR1_ALL_SENT;

	LOG("Z80DART \"%s\" Channel %c : Data Register Write '%02x'\n", owner()->tag(), 'A' + m_index, data);
}


//-------------------------------------------------
//  receive_data - receive data word
//-------------------------------------------------

void z80dart_channel::receive_data(uint8_t data)
{
	LOG("Z80DART \"%s\" Channel %c : Receive Data Byte '%02x'\n", owner()->tag(), 'A' + m_index, data);

	if (m_rx_data_fifo.full())
	{
		// receive overrun error detected
		m_rx_error |= RR1_RX_OVERRUN_ERROR;

		switch (m_wr[1] & WR1_RX_INT_MODE_MASK)
		{
		case WR1_RX_INT_FIRST:
			if (!m_rx_first)
			{
				m_uart->trigger_interrupt(m_index, INT_SPECIAL);
			}
			break;

		case WR1_RX_INT_ALL_PARITY:
		case WR1_RX_INT_ALL:
			m_uart->trigger_interrupt(m_index, INT_SPECIAL);
			break;
		}
		// overwrite last character/error with received character and error status into FIFO
		m_rx_data_fifo.poke(data);
		m_rx_error_fifo.poke(m_rx_error);
	}
	else
	{
		// store received character and error status into FIFO
		m_rx_data_fifo.enqueue(data);
		m_rx_error_fifo.enqueue(m_rx_error);
	}

	m_rr[0] |= RR0_RX_CHAR_AVAILABLE;

	// receive interrupt
	switch (m_wr[1] & WR1_RX_INT_MODE_MASK)
	{
	case WR1_RX_INT_FIRST:
		if (m_rx_first)
		{
			m_uart->trigger_interrupt(m_index, INT_RECEIVE);

			m_rx_first = 0;
		}
		break;

	case WR1_RX_INT_ALL_PARITY:
	case WR1_RX_INT_ALL:
		m_uart->trigger_interrupt(m_index, INT_RECEIVE);
		break;
	}
}


//-------------------------------------------------
//  cts_w - clear to send handler
//-------------------------------------------------

WRITE_LINE_MEMBER( z80dart_channel::cts_w )
{
	LOG("Z80DART \"%s\" Channel %c : CTS %u\n", owner()->tag(), 'A' + m_index, state);

	if (m_cts != state)
	{
		// enable transmitter if in auto enables mode
		if (!state)
			if (m_wr[3] & WR3_AUTO_ENABLES)
				m_wr[5] |= WR5_TX_ENABLE;

		// set clear to send
		m_cts = state;

		if (!m_rx_rr0_latch)
		{
			if (!m_cts)
				m_rr[0] |= RR0_CTS;
			else
				m_rr[0] &= ~RR0_CTS;

			// trigger interrupt
			if (m_wr[1] & WR1_EXT_INT_ENABLE)
			{
				// trigger interrupt
				m_uart->trigger_interrupt(m_index, INT_EXTERNAL);

				// latch read register 0
				m_rx_rr0_latch = 1;
			}
		}
	}
}


//-------------------------------------------------
//  dcd_w - data carrier detected handler
//-------------------------------------------------

WRITE_LINE_MEMBER( z80dart_channel::dcd_w )
{
	LOG("Z80DART \"%s\" Channel %c : DCD %u\n", owner()->tag(), 'A' + m_index, state);

	if (m_dcd != state)
	{
		// enable receiver if in auto enables mode
		if (!state)
			if (m_wr[3] & WR3_AUTO_ENABLES)
				m_wr[3] |= WR3_RX_ENABLE;

		// set data carrier detect
		m_dcd = state;

		if (!m_rx_rr0_latch)
		{
			if (m_dcd)
				m_rr[0] |= RR0_DCD;
			else
				m_rr[0] &= ~RR0_DCD;

			if (m_wr[1] & WR1_EXT_INT_ENABLE)
			{
				// trigger interrupt
				m_uart->trigger_interrupt(m_index, INT_EXTERNAL);

				// latch read register 0
				m_rx_rr0_latch = 1;
			}
		}
	}
}


//-------------------------------------------------
//  ri_w - ring indicator handler
//-------------------------------------------------

WRITE_LINE_MEMBER( z80dart_channel::ri_w )
{
	LOG("Z80DART \"%s\" Channel %c : RI %u\n", owner()->tag(), 'A' + m_index, state);

	if (m_ri != state)
	{
		// set ring indicator state
		m_ri = state;

		if (!m_rx_rr0_latch)
		{
			if (m_ri)
				m_rr[0] |= RR0_RI;
			else
				m_rr[0] &= ~RR0_RI;

			if (m_wr[1] & WR1_EXT_INT_ENABLE)
			{
				// trigger interrupt
				m_uart->trigger_interrupt(m_index, INT_EXTERNAL);

				// latch read register 0
				m_rx_rr0_latch = 1;
			}
		}
	}
}


//-------------------------------------------------
//  sync_w - sync handler
//-------------------------------------------------

WRITE_LINE_MEMBER( z80dart_channel::sync_w )
{
	LOG("Z80DART \"%s\" Channel %c : SYNC %u\n", owner()->tag(), 'A' + m_index, state);
}


//-------------------------------------------------
//  rxc_w - receive clock
//-------------------------------------------------

WRITE_LINE_MEMBER( z80dart_channel::rxc_w )
{
	//LOG("Z80DART \"%s\" Channel %c : Receiver Clock Pulse\n", owner()->tag(), m_index + 'A');
	int clocks = get_clock_mode();
	if (clocks == 1)
		rx_clock_w(state);
	else if(state)
	{
		rx_clock_w(m_rx_clock < clocks/2);

		m_rx_clock++;
		if (m_rx_clock == clocks)
			m_rx_clock = 0;

	}
}


//-------------------------------------------------
//  txc_w - transmit clock
//-------------------------------------------------

WRITE_LINE_MEMBER( z80dart_channel::txc_w )
{
	//LOG("Z80DART \"%s\" Channel %c : Transmitter Clock Pulse\n", owner()->tag(), m_index + 'A');
	int clocks = get_clock_mode();
	if (clocks == 1)
		tx_clock_w(state);
	else if(state)
	{
		tx_clock_w(m_tx_clock < clocks/2);

		m_tx_clock++;
		if (m_tx_clock == clocks)
			m_tx_clock = 0;

	}
}


//-------------------------------------------------
//  update_serial -
//-------------------------------------------------

void z80dart_channel::update_serial()
{
	int data_bit_count = get_rx_word_length();
	stop_bits_t stop_bits = get_stop_bits();

	parity_t parity;
	if (m_wr[4] & WR4_PARITY_ENABLE)
	{
		if (m_wr[4] & WR4_PARITY_EVEN)
			parity = PARITY_EVEN;
		else
			parity = PARITY_ODD;
	}
	else
		parity = PARITY_NONE;

	set_data_frame(1, data_bit_count, parity, stop_bits);

	int clocks = get_clock_mode();

	if (m_rxc > 0)
	{
		set_rcv_rate(m_rxc / clocks);
	}

	if (m_txc > 0)
	{
		set_tra_rate(m_txc / clocks);
	}
	receive_register_reset(); // if stop bits is changed from 0, receive register has to be reset
}


//-------------------------------------------------
//  set_dtr -
//-------------------------------------------------

void z80dart_channel::set_dtr(int state)
{
	m_dtr = state;

	if (m_index == z80dart_device::CHANNEL_A)
		m_uart->m_out_dtra_cb(m_dtr);
	else
		m_uart->m_out_dtrb_cb(m_dtr);
}


//-------------------------------------------------
//  set_rts -
//-------------------------------------------------

void z80dart_channel::set_rts(int state)
{
	if (m_index == z80dart_device::CHANNEL_A)
		m_uart->m_out_rtsa_cb(state);
	else
		m_uart->m_out_rtsb_cb(state);
}


//-------------------------------------------------
//  write_rx -
//-------------------------------------------------

WRITE_LINE_MEMBER(z80dart_channel::write_rx)
{
	m_rxd = state;
	//only use rx_w when self-clocked
	if(m_rxc)
		device_serial_interface::rx_w(state);
}