summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/mc14411.cpp
blob: d8ce720a0f306ca513e669f2c55cd7199ba0c926 (plain) (tree)
1
2
3
4
5
6



                                                                      
                                       
 






                                                                    
 










                                                                    









                                                                            
                                                                 


                                          
                                     


                     
                                                                                         



                                                         

                                    

      
 
                                                                            
                   
                                                                            

                                                   
                                                                           






















                                                                      





                                                                            
 
                         





                                                                                    
 












                                                                                                                                       
 


                                                   
 



                                   
                                                   

                                          

                                                               
         
 

                                   
                                         

                                                    
















                                                   
 














                                                          




                                                   












                                                       
                                                                                    

                                                                                                       

 
 
                                                                          
                                                                          
                                                                
                                                                          
 



                                   
                                                   


                                              
                                                                                                    
                                                                                   








                                                                                                                          
 


                                                   

                                                                                                 
 
                                              
         
                                                         


                                      






                                                                                                                                                                     


            
                                                   


         
 
                                                          
                                                         
                                                          

                                                          

                                            


                                              
                                    
         

                                       


         
 
                                                  































                                                                                  
                                       
                                                  

                                          



                                               
 









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

    Motorola MC14411 Bit Rate Generator

    This CMOS device utilizes a frequency divider network to provide
    sixteen different clock outputs (50% duty cycle). A 1.8432 MHz
    crystal oscillator normally provides the clock source for the
    network, though an external clock may also be applied to pin 21.
    A 2-bit address is used to select one of four rate multipliers
    for the F1-F14 outputs (X64, X16, X8, X1). The undivided input
    frequency is output on F16 (buffered).

    The base dividers for the F12 and F13 outputs do not go into
    1.8432 * 10^6 evenly; the frequencies shown in the table below
    have accordingly been rounded off to five figures. The X64
    frequencies for these two outputs are given on the Motorola
    datasheet as 8613.2 and 7035.5, probably incorrectly calculated.

    To lighten the burden on MAME's scheduler, the emulation of this
    device assumes that not all timer outputs will be needed at any
    given time, and only emulates the clocks actually in use. The
    special enable method intended to be used when several outputs
    are multiplexed through external switches.

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

#include "emu.h"
#include "mc14411.h"

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

//#define LOG_GENERAL (1U <<  0) // Already defined in logmacro.h
#define LOG_SETUP   (1U <<  1)

//#define VERBOSE  (LOG_GENERAL|LOG_SETUP)
//#define LOG_OUTPUT_STREAM std::cout

#include "logmacro.h"

//#define LOG(...) LOGMASKED(LOG_GENERAL,   __VA_ARGS__) // Already defined in logmacro.h
#define LOGSETUP(...) LOGMASKED(LOG_SETUP,   __VA_ARGS__)

#ifdef _MSC_VER
#define FUNCNAME __func__
#else
#define FUNCNAME __PRETTY_FUNCTION__
#endif


/***************************************************************************
    INTERNAL TABLES
***************************************************************************/

const int mc14411_device::s_counter_divider[16] = {
		 ////////// X64 /////// X16 /////// X8 //////// X1 ////////
	  3, // F1:     614.4 kHz   153.6 kHz   76800 Hz    9600 Hz
	  4, // F2:     460.8 kHz   115.2 kHz   57600 Hz    7200 Hz
	  6, // F3:     307.2 kHz   76800 Hz    36400 Hz    4800 Hz
	  8, // F4:     230.4 kHz   57600 Hz    28800 Hz    3600 Hz
	 12, // F5:     153.6 kHz   38400 Hz    19200 Hz    2400 Hz
	 16, // F6:     115.2 kHz   28800 Hz    14400 Hz    1800 Hz
	 24, // F7:     76800 Hz    19200 Hz    9600 Hz     1200 Hz
	 48, // F8:     38400 Hz    9600 Hz     4800 Hz     600 Hz
	 96, // F9:     19200 Hz    4800 Hz     2400 Hz     300 Hz
	144, // F10:    12800 Hz    3200 Hz     1600 Hz     200 Hz
	192, // F11:    9600 Hz     2400 Hz     1200 Hz     150 Hz
	214, // F12:    8613.1 Hz   2153.3 Hz   1076.6 Hz   134.58 Hz
	262, // F13:    7035.1 Hz   1758.8 Hz   879.39 Hz   109.92 Hz
	384, // F14:    4800.0 Hz   1200 Hz     600 Hz      75 Hz
	  2, // F15:    921.6 kHz   921.6 kHz   921.6 kHz   921.6 kHz
	  1  // F16:    1.8432 MHz  1.8432 MHz  1.8432 MHz  1.8432 MHz
};

const int mc14411_device::s_divider_select[4] = {
	64, // RSB=0, RSA=0 - X1
	 8, // RSB=0, RSA=1 - X8
	 4, // RSB=1, RSA=0 - X16
	 1  // RSB=1, RSA=1 - X64
};


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

// device type definition
DEFINE_DEVICE_TYPE(MC14411, mc14411_device, "mc14411", "MC14411 Bit Rate Generator")


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

mc14411_device::mc14411_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: mc14411_device(mconfig, MC14411, tag, owner, clock)
{
}

mc14411_device::mc14411_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, type, tag, owner, clock)
	, m_out_fx_cbs{*this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this }
	, m_divider(0)
	, m_reset(CLEAR_LINE)
{
}


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

void mc14411_device::device_start()
{
	LOGSETUP("%s\n", FUNCNAME);

	for (int i = TIMER_F1; i <= TIMER_F16; i++)
	{
		m_out_fx_cbs[i].resolve();
		m_fx_timer[i] = timer_alloc(i);
		m_timer_enabled[i] = !m_out_fx_cbs[i].isnull();
	}

	save_item(NAME(m_divider));
	save_item(NAME(m_reset));
	save_item(NAME(m_timer_enabled));

	m_reset_timer = timer_alloc(TIMER_ID_RESET);
}


//-------------------------------------------------
//  device_clock_changed - notifier called upon
//  establishment or alteration of the device clock
//-------------------------------------------------

void mc14411_device::device_clock_changed()
{
	for (int i = TIMER_F1; i <= TIMER_F16; i++)
	{
		if (m_timer_enabled[i])
			arm_timer(i);
	}
}


//-------------------------------------------------
//  timer_enable - enable or disable one timer
//-------------------------------------------------

void mc14411_device::timer_enable(timer_id i, bool enable)
{
	assert(i >= TIMER_F1 && i <= TIMER_F16);
	m_timer_enabled[i] = enable;

	if (!enable)
		m_fx_timer[i]->enable(false);
	else if (!m_fx_timer[i]->enabled())
		arm_timer(i);
}

void mc14411_device::timer_disable_all()
{
	for (int i = TIMER_F1; i <= TIMER_F16; i++)
		timer_enable((timer_id) i, false);
}

//-------------------------------------------------
//  arm_timer - arm the timer based on selected
//  divider and crystal value
//-------------------------------------------------

void mc14411_device::arm_timer(int i)
{
	assert(!m_out_fx_cbs[i].isnull());

	int divider = s_counter_divider[i];
	if (i < TIMER_F15)
		divider *= s_divider_select[m_divider];
	attotime half_cycle = clocks_to_attotime(divider) / 2; // 2 flanks per cycle
	m_fx_timer[i]->adjust(half_cycle, i, half_cycle);
	LOGSETUP(" - arming timer for F%d at %fHz (/%d)\n", i + 1, double(clock()) / divider, divider);
}


//------------------------------------------------------------------------
//  device_reset - is called by the mame framework or by the owning device
//  driver or by ASSERTING the reset line through set_reset_line
//------------------------------------------------------------------------

void mc14411_device::device_reset()
{
	LOGSETUP("%s\n", FUNCNAME);

	for (int i = TIMER_F1; i <= TIMER_F16; i++)
	{
		if (!m_out_fx_cbs[i].isnull())
		{
			// Reset line according to datasheet and remember it for transitions to come
			(m_out_fx_cbs[i])(m_fx_state[i] = (i < TIMER_F15 ? 0 : 1));
		}
	}

	if (m_reset == ASSERT_LINE)
	{
		m_reset_timer->adjust(attotime::from_nsec((double)900), TIMER_ID_RESET, attotime::from_nsec((double)900));
	}
}


//-------------------------------------------------
//  device_timer - handler timer events
//-------------------------------------------------

void mc14411_device::device_timer(emu_timer &timer, device_timer_id id, int32_t param, void *ptr)
{
	if (id >= TIMER_F1 && id <= TIMER_F16)
	{
		(m_out_fx_cbs[id])(m_fx_state[id]++ & 1);
	}
	else if (id == TIMER_ID_RESET)
	{
		// NOTE: This check could be triggered by either faulty hardware design or non accurate emulation so is just informative if the reset line is handled
		//       explicitelly instead of relying on calling device_reset
		if (!(m_reset == ASSERT_LINE))
		{
			LOG("Reset pulse is too short, should be 900nS minimum");
			logerror("Reset pulse is too short, should be 900nS minimum");
		}
	}
	else
	{
		LOG("Unhandled Timer ID %d\n", id);
	}
}


//--------------------------------------------------------
//  rate_select_w - implements the RSA and RSB input pins
// TODO: Needs to check real device behaviour how changing
//       divider at run time affects wave forms
//--------------------------------------------------------

WRITE8_MEMBER(mc14411_device::rate_select_w)
{
	LOGSETUP("%s %02x\n", FUNCNAME, data);

	if (m_divider != (data & 3))
	{
		m_divider = data & 3;
		notify_clock_changed();
	}
}


//------------------------------------------------
//  rsa_w - set RSA input line
//------------------------------------------------

WRITE_LINE_MEMBER(mc14411_device::rsa_w)
{
	LOGSETUP("%s %02x\n", FUNCNAME, state);

	if ((m_divider & RSA) != (state == ASSERT_LINE ? RSA : 0))
	{
		m_divider = (m_divider & ~RSA) | (state == ASSERT_LINE ? RSA : 0);
		notify_clock_changed();
	}
}


//------------------------------------------------
//  rsb_w - set RSB input line
//------------------------------------------------

WRITE_LINE_MEMBER(mc14411_device::rsb_w)
{
	LOGSETUP("%s %02x\n", FUNCNAME, state);

	if ((m_divider & RSB) != (state == ASSERT_LINE ? RSB : 0))
	{
		m_divider = (m_divider & ~RSB) | (state == ASSERT_LINE ? RSB : 0);
		notify_clock_changed();
	}
}


//------------------------------------------------
//  reset_w - software controlled reset
//------------------------------------------------

WRITE_LINE_MEMBER(mc14411_device::reset_w)
{
	LOGSETUP("%s %02x\n", FUNCNAME, state);

	m_reset = state;

	if (m_reset == ASSERT_LINE)
	{
		LOGSETUP(" - Asserting reset\n");
		device_reset();
	}
	else
	{
		LOGSETUP(" - Clearing reset\n");
	}
}