summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/mc6846.cpp
blob: 75c76b8d283bdf1fc0d3cc434eccdacd3950a4b5 (plain) (tree)
1
2

                                 






















                                                                               

                     

 
                                                                      
 

                                             
                                                                     
                        
 

                                                      
 
                           
 
                                    
 
 
                                                                                
 
                                                                                                             
                                                       





                             

 
 
                                                   

                                                   
 



                                                                                                                          
 


                                                         
 
                                                    
                                                 

                                                        
                                                  
 
                             
                           

















                                              
 


                                                   
 
                                  
 















                                 

                      

                            


 
                                        
 
                              
         
                                                           
                                                          

            
                                



 
                                       
 

                                      


                                                                            
                        
                               
         
                                                                         
                                                                  
                                                                                  
                                


                  
                              

                                         


            
                               

                                         




         
                                       
 
                      
                               
         
                                                                                       
                                
         
                                     
                                    



 
                                         
 
                                          
                                                                                                                                  
 
                            





                                                                          

                                   
                          


                                    
                          
                                                              






                                                                           

                                    


                       
                                                     
                            
 


                     





                                                                       
                                                    
 
                                         
 
                                                                                                                                

                              
                           
 
                            





                                                                          
                                  


                                    
                          


                                              
                                                 



                                                                           
                                    
                                    


                       
                                                     
 


                     



 
                                                      
 
                                                                                  

                                                                      

                     






                                                                       
                                 
 



                         
                                                                                         
                                                                                           
                                                        
                                                                                



                                                 

               
                                                                                                                           
                             

               
                                                                                                                           
                             

               
                                                                                                                           
                                       
                 






                                                   



                            
                                                                                                                          
                             

               
                                                                                                                                           
                                           
                 

                                     
                 

                                         

               
                                                                                                                                              
                                           
                 

                                     
                 

                                         

                
                                                                                                       





                 
                                   
 



















                                                                                             
                                                                              
                                                                                                 
                                                                                           

         
                     

                               



                             

                       
                                                                                                     



                                
                                                    
                                                     
                                                          

                    
                                                                                                            



               
                                                                                                                           
                                       
                 
                                     

                                                                           



                      
                                                                                                                                               
                                       
                 
                                     

                                                                           
                                                                  
                         
                                            
                                                                                                                                    
                         
                                                                  
                         
                                            
                                                                                                                                    
                         


                                                 









                                                                                   
                                                                                                 
                                                                                                 

                                                                                  
                                                               
 

                                

                                                                          

                                           
                                           




                                            



                                          

                                                
                 
                             



               
                                  


                      
                                                                  
                                                                                                                              
                                    

                                                  




                                           
                                                    

                                               



                      
                                                                                                        








                                                                       
                                           
 
                            
                            
                       
                     
                                                                                       
                                                               
         

                             


         
                                           
 
                            
                            
                       
                     
                                                                                      
                         
         
                                                                             
                 

                                     









                                                                       
                                        
 




                    
                                       
 




                   
                                       
 
                         

 
 
 
                                    
 
                        
 
// license:BSD-3-Clause
// copyright-holders:Antoine Mine
/**********************************************************************

  Copyright (C) Antoine Mine' 2006

  Motorola 6846 emulation.

  The MC6846 chip provides ROM (2048 bytes), I/O (8-bit directional data port +
  2 control lines) and a programmable timer.
  It may be interfaced with a M6809 cpu.
  It is used in some Thomson computers.

  Not yet implemented:
  - external clock (CTC)
  - latching of port on CP1
  - gate input (CTG)
  - timer comparison modes (frequency and pulse width)
  - CP2 acknowledge modes

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

#include "emu.h"
#include "mc6846.h"

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


/******************* utility function and macros ********************/

#define PORT                                \
	((m_pdr & m_ddr) |                  \
		((!m_in_port_cb.isnull() ? m_in_port_cb( 0 ) : 0) & \
		~m_ddr))

#define CTO                             \
	((MODE == 0x30 || (m_tcr & 0x80)) ? m_cto : 0)

#define MODE (m_tcr & 0x38)

#define FACTOR ((m_tcr & 4) ? 8 : 1)


DEFINE_DEVICE_TYPE(MC6846, mc6846_device, "mc6846", "MC6846 Programmable Timer")

mc6846_device::mc6846_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, MC6846, tag, owner, clock),
	m_out_port_cb(*this),
	m_out_cp1_cb(*this),
	m_out_cp2_cb(*this),
	m_in_port_cb(*this),
	m_out_cto_cb(*this),
	m_irq_cb(*this)
{
}

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

void mc6846_device::device_start()
{
	m_interval = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(mc6846_device::timer_expire), this));
	m_one_shot = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(mc6846_device::timer_one_shot), this));

	m_out_port_cb.resolve();  /* 8-bit output */
	m_out_cp1_cb.resolve_safe();   /* 1-bit output */
	m_out_cp2_cb.resolve();   /* 1-bit output */

	/* CPU read from the outside through chip */
	m_in_port_cb.resolve(); /* 8-bit input */

	/* asynchronous timer output to outside world */
	m_out_cto_cb.resolve(); /* 1-bit output */

	/* timer interrupt */
	m_irq_cb.resolve();

	save_item(NAME(m_csr));
	save_item(NAME(m_pcr));
	save_item(NAME(m_ddr));
	save_item(NAME(m_pdr));
	save_item(NAME(m_tcr));
	save_item(NAME(m_cp1));
	save_item(NAME(m_cp2));
	save_item(NAME(m_cp2_cpu));
	save_item(NAME(m_cto));
	save_item(NAME(m_time_MSB));
	save_item(NAME(m_csr0_to_be_cleared));
	save_item(NAME(m_csr1_to_be_cleared));
	save_item(NAME(m_csr2_to_be_cleared));
	save_item(NAME(m_latch));
	save_item(NAME(m_preset));
	save_item(NAME(m_timer_started));
}

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

void mc6846_device::device_reset()
{
	m_cto   = 0;
	m_csr   = 0;
	m_pcr   = 0x80;
	m_ddr   = 0;
	m_pdr   = 0;
	m_tcr   = 1;
	m_cp1   = 0;
	m_cp2   = 0;
	m_cp2_cpu  = 0;
	m_latch    = 0xffff;
	m_preset   = 0xffff;
	m_time_MSB = 0;
	m_csr0_to_be_cleared = 0;
	m_csr1_to_be_cleared = 0;
	m_csr2_to_be_cleared = 0;
	m_timer_started = 0;
	m_old_cif = 0;
	m_old_cto = 0;
	m_interval->reset();
	m_one_shot->reset();
}


inline uint16_t mc6846_device::counter()
{
	if ( m_timer_started )
	{
		attotime delay = m_interval ->remaining( );
		return attotime_to_clocks(delay) / FACTOR;
	}
	else
		return m_preset;
}



inline void mc6846_device::update_irq()
{
	int cif = 0;
	/* composite interrupt flag */
	if ( ( (m_csr & 1) && (m_tcr & 0x40) ) ||
			( (m_csr & 2) && (m_pcr & 1) ) ||
			( (m_csr & 4) && (m_pcr & 8) && ! (m_pcr & 0x20) ) )
		cif = 1;
	if ( m_old_cif != cif )
	{
		LOG( "%f: mc6846 interrupt %i (time=%i cp1=%i cp2=%i)\n",
				machine().time().as_double(), cif,
				m_csr & 1, (m_csr >> 1 ) & 1, (m_csr >> 2 ) & 1 );
		m_old_cif = cif;
	}
	if ( cif )
	{
		m_csr |= 0x80;
		if ( !m_irq_cb.isnull() )
			m_irq_cb( 1 );
	}
	else
	{
		m_csr &= ~0x80;
		if ( !m_irq_cb.isnull() )
			m_irq_cb( 0 );
	}
}



inline void mc6846_device::update_cto()
{
	int cto = CTO;
	if ( cto != m_old_cto )
	{
		LOG( "%f: mc6846 CTO set to %i\n", machine().time().as_double(), cto );
		m_old_cto = cto;
	}
	if ( !m_out_cto_cb.isnull() )
		m_out_cto_cb( cto );
}



inline void mc6846_device::timer_launch()
{
	int delay = FACTOR * (m_preset+1);
	LOG( "%f: mc6846 timer launch called, mode=%i, preset=%i (x%i)\n", machine().time().as_double(), MODE, m_preset, FACTOR );

	if ( ! (m_tcr & 2) )
	{
		logerror( "mc6846 external clock CTC not implemented\n" );
	}

	switch( MODE )
	{
	case 0x00:
	case 0x10: /* continuous */
		m_cto = 0;
		break;

	case 0x20: /* single-shot */
		m_cto = 0;
		m_one_shot->reset(clocks_to_attotime(FACTOR));
		break;

	case 0x30:  /* cascaded single-shot */
		break;

	default:
		logerror( "mc6846 timer mode %i not implemented\n", MODE );
		m_interval->reset();
		m_timer_started = 0;
		return;
	}

	m_interval->reset(clocks_to_attotime(delay));
	m_timer_started = 1;

	m_csr &= ~1;
	update_cto();
	update_irq();
}



/******************* timer callbacks *********************************/

TIMER_CALLBACK_MEMBER( mc6846_device::timer_expire )
{
	int delay = FACTOR * (m_latch+1);

	LOG( "%f: mc6846 timer expire called, mode=%i, latch=%i (x%i)\n", machine().time().as_double(), MODE, m_latch, FACTOR );

	/* latch => counter */
	m_preset = m_latch;

	if ( ! (m_tcr & 2) )
		logerror( "mc6846 external clock CTC not implemented\n" );

	switch ( MODE )
	{
	case 0x00:
	case 0x10: /* continuous */
		m_cto = 1 ^ m_cto;
		break;

	case 0x20: /* single-shot */
		m_cto = 0;
		break;

	case 0x30:  /* cascaded single-shot */
		m_cto = ( m_tcr & 0x80 ) ? 1 : 0;
		break;

	default:
		logerror( "mc6846 timer mode %i not implemented\n", MODE );
		m_interval->reset();
		m_timer_started = 0;
		return;
	}

	m_interval->reset(clocks_to_attotime(delay));

	m_csr |= 1;
	update_cto();
	update_irq();
}



TIMER_CALLBACK_MEMBER( mc6846_device::timer_one_shot )
{
	LOG( "%f: mc6846 timer one shot called\n", machine().time().as_double() );

	/* 1 micro second after one-shot launch, we put cto to high */
	m_cto = 1;
	update_cto();
}



/************************** CPU interface ****************************/


READ8_MEMBER(mc6846_device::read)
{
	switch ( offset )
	{
	case 0:
	case 4:
		LOG( "%s %f: mc6846 CSR read $%02X intr=%i (timer=%i, cp1=%i, cp2=%i)\n",
				machine().describe_context(), machine().time().as_double(),
				m_csr, (m_csr >> 7) & 1,
				m_csr & 1, (m_csr >> 1) & 1, (m_csr >> 2) & 1 );
		m_csr0_to_be_cleared = m_csr & 1;
		m_csr1_to_be_cleared = m_csr & 2;
		m_csr2_to_be_cleared = m_csr & 4;
		return m_csr;

	case 1:
		LOG( "%s %f: mc6846 PCR read $%02X\n", machine().describe_context(), machine().time().as_double(), m_pcr );
		return m_pcr;

	case 2:
		LOG( "%s %f: mc6846 DDR read $%02X\n", machine().describe_context(), machine().time().as_double(), m_ddr );
		return m_ddr;

	case 3:
		LOG( "%s %f: mc6846 PORT read $%02X\n", machine().describe_context(), machine().time().as_double(), PORT );
		if ( ! (m_pcr & 0x80) )
		{
			if ( m_csr1_to_be_cleared )
				m_csr &= ~2;
			if ( m_csr2_to_be_cleared )
				m_csr &= ~4;
			update_irq();
			m_csr1_to_be_cleared = 0;
			m_csr2_to_be_cleared = 0;
		}
		return PORT;

	case 5:
		LOG( "%s %f: mc6846 TCR read $%02X\n",machine().describe_context(), machine().time().as_double(), m_tcr );
		return m_tcr;

	case 6:
		LOG( "%s %f: mc6846 COUNTER hi read $%02X\n", machine().describe_context(), machine().time().as_double(), counter() >> 8 );
		if ( m_csr0_to_be_cleared )
		{
			m_csr &= ~1;
			update_irq();
		}
		m_csr0_to_be_cleared = 0;
		return counter() >> 8;

	case 7:
		LOG( "%s %f: mc6846 COUNTER low read $%02X\n", machine().describe_context(), machine().time().as_double(), counter() & 0xff );
		if ( m_csr0_to_be_cleared )
		{
			m_csr &= ~1;
			update_irq();
		}
		m_csr0_to_be_cleared = 0;
		return counter() & 0xff;

	default:
		logerror( "%s mc6846 invalid read offset %i\n", machine().describe_context(), offset );
	}
	return 0;
}



WRITE8_MEMBER(mc6846_device::write)
{
	switch ( offset )
	{
	case 0:
	case 4:
		/* CSR is read-only */
		break;

	case 1:
	{
		static const char *const cp2[8] =
		{
			"in,neg-edge", "in,neg-edge,intr", "in,pos-edge", "in,pos-edge,intr",
			"out,intr-ack", "out,i/o-ack", "out,0", "out,1"
		};
		static const char *const cp1[8] =
		{
			"neg-edge", "neg-edge,intr", "pos-edge", "pos-edge,intr",
			"latched,neg-edge", "latched,neg-edge,intr",
			"latcged,pos-edge", "latcged,pos-edge,intr"
		};
		LOG( "%s %f: mc6846 PCR write $%02X reset=%i cp2=%s cp1=%s\n",
				machine().describe_context(), machine().time().as_double(), data,
				(data >> 7) & 1, cp2[ (data >> 3) & 7 ], cp1[ data & 7 ] );

	}
	m_pcr = data;
	if ( data & 0x80 )
	{      /* data reset */
		m_pdr = 0;
		m_ddr = 0;
		m_csr &= ~6;
		update_irq();
	}
	if ( data & 4 )
		logerror( "%s mc6846 CP1 latching not implemented\n", machine().describe_context() );
	if (data & 0x20)
	{
		if (data & 0x10)
		{
			m_cp2_cpu = (data >> 3) & 1;
			if ( !m_out_cp2_cb.isnull() )
				m_out_cp2_cb( m_cp2_cpu );
		}
		else
			logerror( "%s mc6846 acknowledge not implemented\n", machine().describe_context() );
	}
	break;

	case 2:
		LOG( "%s %f: mc6846 DDR write $%02X\n", machine().describe_context(), machine().time().as_double(), data );
		if ( ! (m_pcr & 0x80) )
		{
			m_ddr = data;
			if ( !m_out_port_cb.isnull() )
				m_out_port_cb( (offs_t) 0, m_pdr & m_ddr );
		}
		break;

	case 3:
		LOG( "%s %f: mc6846 PORT write $%02X (mask=$%02X)\n", machine().describe_context(), machine().time().as_double(), data,m_ddr );
		if ( ! (m_pcr & 0x80) )
		{
			m_pdr = data;
			if ( !m_out_port_cb.isnull() )
				m_out_port_cb( (offs_t) 0, m_pdr & m_ddr );
			if ( m_csr1_to_be_cleared && (m_csr & 2) )
			{
				m_csr &= ~2;
				LOG( "%s %f: mc6846 CP1 intr reset\n", machine().describe_context(), machine().time().as_double() );
			}
			if ( m_csr2_to_be_cleared && (m_csr & 4) )
			{
				m_csr &= ~4;
				LOG( "%s %f: mc6846 CP2 intr reset\n", machine().describe_context(), machine().time().as_double() );
			}
			m_csr1_to_be_cleared = 0;
			m_csr2_to_be_cleared = 0;
			update_irq();
		}
		break;

	case 5:
	{
		static const char *const mode[8] =
			{
				"continuous", "cascaded", "continuous", "one-shot",
				"freq-cmp", "freq-cmp", "pulse-cmp", "pulse-cmp"
			};
		LOG( "%s %f: mc6846 TCR write $%02X reset=%i clock=%s scale=%i mode=%s out=%s\n",
				machine().describe_context(), machine().time().as_double(), data,
				(data >> 7) & 1, (data & 0x40) ? "extern" : "sys",
				(data & 0x40) ? 1 : 8, mode[ (data >> 1) & 7 ],
				(data & 1) ? "enabled" : "0" );

		m_tcr = data;
		if ( m_tcr & 1 )
		{
			/* timer preset = initialization without launch */
			m_preset = m_latch;
			m_csr &= ~1;
			if ( MODE != 0x30 )
				m_cto = 0;
			update_cto();
			m_interval->reset();
			m_one_shot->reset();
			m_timer_started = 0;
		}
		else
		{
			/* timer launch */
			if ( ! m_timer_started )
				timer_launch();
		}
		update_irq();
	}
	break;

	case 6:
		m_time_MSB = data;
		break;

	case 7:
		m_latch = ( ((uint16_t) m_time_MSB) << 8 ) + data;
		LOG( "%s %f: mc6846 COUNT write %i\n", machine().describe_context(), machine().time().as_double(), m_latch  );
		if (!(m_tcr & 0x38))
		{
			/* timer initialization */
			m_preset = m_latch;
			m_csr &= ~1;
			update_irq();
			m_cto = 0;
			update_cto();
			/* launch only if started */
			if (!(m_tcr & 1))
				timer_launch();
		}
		break;

	default:
		logerror( "%s mc6846 invalid write offset %i\n", machine().describe_context(), offset );
	}
}



/******************** outside world interface ************************/



void mc6846_device::set_input_cp1(int data)
{
	data = (data != 0 );
	if ( data == m_cp1 )
		return;
	m_cp1 = data;
	LOG( "%f: mc6846 input CP1 set to %i\n",  machine().time().as_double(), data );
	if (( data &&  (m_pcr & 2)) || (!data && !(m_pcr & 2)))
	{
		m_csr |= 2;
		update_irq();
	}
}

void mc6846_device::set_input_cp2(int data)
{
	data = (data != 0 );
	if ( data == m_cp2 )
		return;
	m_cp2 = data;
	LOG( "%f: mc6846 input CP2 set to %i\n", machine().time().as_double(), data );
	if (m_pcr & 0x20)
	{
		if (( data &&  (m_pcr & 0x10)) || (!data && !(m_pcr & 0x10)))
		{
			m_csr |= 4;
			update_irq();
		}
	}
}



/************************ accessors **********************************/



uint8_t mc6846_device::get_output_port()
{
	return PORT;
}



uint8_t mc6846_device::get_output_cto()
{
	return CTO;
}



uint8_t mc6846_device::get_output_cp2()
{
	return m_cp2_cpu;
}



uint16_t mc6846_device::get_preset()
{
	return m_preset;
}