summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/pcf8573.cpp
blob: a0d12344db621614eec53cc7ebd1bfb375121628 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14













                                                                      

                          









































                                                                                                               
                          
                                                                            

















                                                                                                         
                                

                                                   
                                                 






































                                                                                                                                       
                                    








                                                                          
                                    








                                                                          
                                     






















                                                                                   




                                                        
                                         
                                                                
                                                 

                                                                           
 















                                                                                                                                 
 

                                                                                 
 




























                                                                                                            
                                                              
 




                                                                                            
 
                                                                                                                                  
 






                                                                                                                                                             
 

                                                                                                                          
                                                         

                                                              
 



                                                                           









                                                           




                                                




                                          



                                                 



















                                                                                                                          











                                                                              
                                                   


                                    
                                                   






                                 
                                     

























                                                               
                           






                                                  
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/*********************************************************************

    Philips PCF8573 Clock and Calendar with Power Fail Detector

    TODO:
    - status bits 3/4 seconds/minutes are not set.

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

#include "emu.h"
#include "pcf8573.h"

#define LOG_DATA (1U << 1)
#define LOG_LINE (1U << 2)

#define VERBOSE (0)
#include "logmacro.h"


DEFINE_DEVICE_TYPE(PCF8573, pcf8573_device, "pcf8573", "PCF8573 RTC with Power Fail Detector")

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

//-------------------------------------------------
//  pcf8573_device - constructor
//-------------------------------------------------

pcf8573_device::pcf8573_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, PCF8573, tag, owner, clock)
	, device_rtc_interface(mconfig, *this)
	, m_comp_cb(*this)
	, m_min_cb(*this)
	, m_sec_cb(*this)
	, m_clock_timer(nullptr)
	, m_slave_address(PCF8573_SLAVE_ADDRESS)
	, m_scl(0)
	, m_sdaw(0)
	, m_sdar(1)
	, m_state(STATE_IDLE)
	, m_bits(0)
	, m_shift(0)
	, m_devsel(0)
	, m_mode_pointer(0)
	, m_address(0)
	, m_status(0)
{
}

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

void pcf8573_device::device_start()
{
	// allocate timers
	m_clock_timer = timer_alloc(FUNC(pcf8573_device::clock_tick), this);
	m_clock_timer->adjust(attotime::from_hz(clock() / 32768), 0, attotime::from_hz(clock() / 32768));

	// state saving
	save_item(NAME(m_scl));
	save_item(NAME(m_sdaw));
	save_item(NAME(m_sdar));
	save_item(NAME(m_state));
	save_item(NAME(m_bits));
	save_item(NAME(m_shift));
	save_item(NAME(m_devsel));
	save_item(NAME(m_mode_pointer));
	save_item(NAME(m_address));
	save_item(NAME(m_status));
	save_item(NAME(m_data));
	save_item(NAME(m_slave_address));
}

//-------------------------------------------------
//  clock_tick - advance the RTC
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(pcf8573_device::clock_tick)
{
	advance_seconds();

	// one pulse per second output
	m_sec_cb(1);
	m_sec_cb(0);
}

void pcf8573_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second)
{
	if (m_data[REG_MINUTES] == convert_to_bcd(minute))
		return;

	// update registers
	set_time_minute(minute);
	set_time_hour(hour);
	set_date_day(day);
	set_date_month(month);

	// one pulse per minute output
	m_min_cb(1);
	m_min_cb(0);

	// comparator output
	if (m_data[REG_HOURS] == m_data[REG_ALARM_HOURS] && m_data[REG_MINUTES] == m_data[REG_ALARM_MINUTES])
	{
		if (BIT(m_status, 2) || (m_data[REG_DAYS] == m_data[REG_ALARM_DAYS] && m_data[REG_MONTHS] == m_data[REG_ALARM_MONTHS]))
		{
			// set COMP flag
			m_status |= 1 << 1;
			m_comp_cb(1);
		}
	}
}

//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

void pcf8573_device::a0_w(int state)
{
	state &= 1;
	if (BIT(m_slave_address, 1) != state)
	{
		LOGMASKED(LOG_LINE, "set a0 %d\n", state );
		m_slave_address = (m_slave_address & 0xfd) | (state << 1);
	}
}

void pcf8573_device::a1_w(int state)
{
	state &= 1;
	if (BIT(m_slave_address, 2) != state)
	{
		LOGMASKED(LOG_LINE, "set a1 %d\n", state );
		m_slave_address = (m_slave_address & 0xfb) | (state << 2);
	}
}

void pcf8573_device::scl_w(int state)
{
	if (m_scl != state)
	{
		m_scl = state;
		LOGMASKED(LOG_LINE, "set_scl_line %d\n", m_scl);

		switch (m_state)
		{
		case STATE_ADDRESS:
		case STATE_MODE:
		case STATE_DATAIN:
			if (m_bits < 8)
			{
				if (m_scl)
				{
					m_shift = ((m_shift << 1) | m_sdaw) & 0xff;
					m_bits++;
				}
			}
			else
			{
				if (m_scl)
				{
					m_bits++;
				}
				else
				{
					if (m_bits == 8)
					{
						switch (m_state)
						{
						case STATE_ADDRESS:
							m_devsel = m_shift;

							if ((m_devsel & 0xfe) != m_slave_address)
							{
								LOGMASKED(LOG_DATA, "address %02x: not this device\n", m_devsel);
								m_state = STATE_IDLE;
							}
							else if ((m_devsel & 1) == 0)
							{
								LOGMASKED(LOG_DATA, "address %02x: write\n", m_devsel);
								m_state = STATE_MODE;
							}
							else
							{
								LOGMASKED(LOG_DATA, "address %02x: read\n", m_devsel);
								m_state = STATE_READSELACK;
							}
							break;

						case STATE_MODE:
							m_mode_pointer = m_shift;

							LOGMASKED(LOG_DATA, "mode pointer %02x\n", m_shift);

							switch (m_mode_pointer & 0x70)
							{
							case 0x00: // execute address
								m_address = m_mode_pointer & 0x07;
								break;
							case 0x10: // read control/status flags
								break;
							case 0x20: // reset prescaler
								set_clock_register(RTC_SECOND, 0);
								adjust_seconds();
								break;
							case 0x30: // time adjust
								adjust_seconds();
								break;
							case 0x40: // reset NODA flag
								m_status &= ~(1 << 2);
								break;
							case 0x50: // set NODA flag
								m_status |= 1 << 2;
								break;
							case 0x60: // reset COMP flag
								m_status &= ~(1 << 1);
								m_comp_cb(0);
								break;
							}

							m_state = STATE_DATAIN;
							break;

						case STATE_DATAIN:
							switch (m_mode_pointer & 0x70)
							{
							case 0x00: // execute address
								m_data[m_address] = m_shift;

								LOGMASKED(LOG_DATA, "data[ %02x ] <- %02x\n", m_address, m_shift);

								switch (m_address)
								{
								case REG_HOURS: set_clock_register(RTC_HOUR, bcd_to_integer(m_data[REG_HOURS])); break;
								case REG_MINUTES: set_clock_register(RTC_MINUTE, bcd_to_integer(m_data[REG_MINUTES])); break;
								case REG_DAYS: set_clock_register(RTC_DAY, bcd_to_integer(m_data[REG_DAYS])); break;
								case REG_MONTHS: set_clock_register(RTC_MONTH, bcd_to_integer(m_data[REG_MONTHS])); break;
								}

								m_address = (m_address & 0x04) | ((m_address + 1) & 0x03);
								break;
							}
							break;
						}

						if( m_state != STATE_IDLE )
						{
							m_sdar = 0 ;
						}
					}
					else
					{
						m_bits = 0;
						m_sdar = 1;
					}
				}
			}
			break;

		case STATE_READSELACK:
			m_bits = 0;
			m_state = STATE_DATAOUT;
			break;

		case STATE_DATAOUT:
			if (m_bits < 8)
			{
				if (m_scl)
				{
					m_bits++;
				}
				else
				{
					if (m_bits == 0)
					{
						switch (m_mode_pointer & 0x70)
						{
						case 0x00: // execute address
							m_shift = m_data[m_address];
							LOGMASKED(LOG_DATA, "data[ %02x ] -> %02x\n", m_address, m_shift);
							m_address = (m_address & 0x04) | ((m_address + 1) & 0x03);
							break;

						case 0x10: // read control/status flags
							m_shift = m_status;
							LOGMASKED(LOG_DATA, "status -> %02x\n", m_address, m_shift);
							break;
						}
					}

					m_sdar = (m_shift >> 7) & 1;

					m_shift = (m_shift << 1) & 0xff;
				}
			}
			else
			{
				if (m_scl)
				{
					if (m_sdaw)
					{
						LOGMASKED(LOG_DATA, "nack\n");
						m_state = STATE_IDLE;
					}

					m_bits = 0;
				}
				else
				{
					m_sdar = 1;
				}
			}
			break;
		}
	}
}

void pcf8573_device::sda_w(int state)
{
	state &= 1;
	if (m_sdaw != state)
	{
		LOGMASKED(LOG_LINE, "set sda %d\n", state);
		m_sdaw = state;

		if (m_scl)
		{
			if (m_sdaw)
			{
				LOGMASKED(LOG_DATA, "stop\n");
				m_state = STATE_IDLE;
			}
			else
			{
				LOGMASKED(LOG_DATA, "start\n");
				m_state = STATE_ADDRESS;
				m_bits = 0;
			}

			m_sdar = 1;
		}
	}
}

int pcf8573_device::sda_r()
{
	int res = m_sdar & 1;

	LOGMASKED(LOG_LINE, "read sda %d\n", res);

	return res;
}