summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/pcf8583.cpp
blob: 47babd1a2accff777108b653be4bf10ded8b69b9 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12





                                                                      





                            






























































































































































































































                                                                                                                                                                                                   
// license:BSD-3-Clause
// copyright-holders:Tim Schuerewegen, Ryan Holtz
/*********************************************************************

    Philips PCF8583 Clock and Calendar with 240 x 8-bit RAM

    TODO:
        - Alarm mode
        - Event-counter mode
        - Clock select
        - Clock output
        - Interrupts

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

#include "emu.h"
#include "pcf8583.h"

DEFINE_DEVICE_TYPE(PCF8583, pcf8583_device, "pcf8583", "PCF8583 RTC with 240x8 RAM")

pcf8583_device::pcf8583_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, PCF8583, tag, owner, clock)
	, device_rtc_interface(mconfig, *this)
	, device_nvram_interface(mconfig, *this)
	, m_irq_callback(*this)
{
}

void pcf8583_device::device_start()
{
	std::fill(std::begin(m_register), std::end(m_register), 0);

	m_timer = timer_alloc(TIMER_TICK);
	m_timer->adjust(attotime::from_hz(100), 0, attotime::from_hz(100));

	save_item(NAME(m_scl));
	save_item(NAME(m_sda));
	save_item(NAME(m_inp));
	save_item(NAME(m_transfer_active));
	save_item(NAME(m_bit_index));
	save_item(NAME(m_irq));
	save_item(NAME(m_data_recv_index));
	save_item(NAME(m_data_recv));
	save_item(NAME(m_mode));
	save_item(NAME(m_pos));
	save_item(NAME(m_write_address));
	save_item(NAME(m_read_address));

	m_irq_callback.resolve_safe();
}

void pcf8583_device::device_reset()
{
	m_scl = 1;
	m_sda = 1;
	m_transfer_active = false;
	m_inp = 0;
	m_mode = RTC_MODE_RECV;
	m_bit_index = 0;
	m_irq = false;
	m_pos = 0;
	clear_rx_buffer();
	set_time(true, get_date_year(), get_date_month(), get_date_day(), 0, get_time_hour(), get_time_minute(), get_time_second());
}

void pcf8583_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch(id)
	{
		case TIMER_TICK:
			if (!BIT(m_data[REG_CONTROL], CONTROL_STOP_BIT))
				advance_hundredths();
			break;
	}
}

void pcf8583_device::advance_hundredths()
{
	uint8_t hundredths = bcd_to_integer(m_data[REG_HUNDREDTHS]);
	hundredths++;
	if (hundredths >= 100)
	{
		hundredths = 0;
		advance_seconds();
		m_irq = !m_irq;
		printf("Toggling IRQ: %d\n", m_irq ? 1 : 0);
		m_irq_callback(m_irq);
	}
	m_data[REG_HUNDREDTHS] = convert_to_bcd(hundredths);
}


void pcf8583_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second)
{
	set_time_second(second);
	set_time_minute(minute);
	set_time_hour(hour);
	set_date_day(day);
	set_date_month(month);
	set_date_year(year);
}

void pcf8583_device::nvram_default()
{
	std::fill(std::begin(m_data), std::end(m_data), 0);
}

void pcf8583_device::nvram_read(emu_file &file)
{
	file.read(m_data, sizeof(m_data));
}

void pcf8583_device::nvram_write(emu_file &file)
{
	file.write(m_data, sizeof(m_data));
}

void pcf8583_device::write_register(uint8_t offset, uint8_t data)
{
	logerror("%s: write_register: address %02x = %02x\n", machine().describe_context(), offset, data);
	m_data[offset] = data;
}

WRITE_LINE_MEMBER(pcf8583_device::scl_w)
{
	if (m_transfer_active && !m_scl && state)
	{
		switch (m_mode)
		{
			case RTC_MODE_RECV:
			{
				logerror("%s: scl_w: Receiving bit %d in receive mode\n", machine().describe_context(), m_sda ? 1 : 0);
				if (m_sda)
					m_data_recv |= (0x80 >> m_bit_index);
				m_bit_index++;

				if (m_bit_index > 8) // ignore ACK bit
				{
					if (m_data_recv_index == 0)
					{
						if (m_data_recv == m_read_address)
						{
							logerror("%s: scl_w: Received byte 0 (%02x), matches read address, entering read/send mode\n", machine().describe_context(), m_data_recv);
							m_mode = RTC_MODE_SEND;
						}
						else if (m_data_recv == m_write_address)
						{
							logerror("%s: scl_w: Received byte 0 (%02x), matches write address, entering read/send mode\n", machine().describe_context(), m_data_recv);
							m_mode = RTC_MODE_RECV;
						}
						else
						{
							logerror("%s: scl_w: Received byte 0 (%02x), unknown address, going idle\n", machine().describe_context(), m_data_recv);
							m_mode = RTC_MODE_NONE;
						}
					}
					else if (m_data_recv_index == 1)
					{
						logerror("%s: scl_w: Received byte 1 (%02x), setting current read/write pos\n", machine().describe_context(), m_data_recv);
						m_pos = m_data_recv;
					}
					else if (m_data_recv_index >= 2)
					{
						logerror("%s: scl_w: Received byte 2+ (%d: %02x), storing to memory\n", machine().describe_context(), m_data_recv_index, m_data_recv);
						write_register(m_pos, m_data_recv);
						m_pos++;
					}

					m_bit_index = 0;
					m_data_recv = 0;
					m_data_recv_index++;
				}
			}
			break;

			case RTC_MODE_SEND:
			{
				if (m_bit_index < 8)
				{
					m_inp = BIT(m_data[m_pos], 7 - m_bit_index);
					logerror("%s: scl_w: In send mode, reading bit %d from ram[0x%02x]=%02x (%d)\n", machine().describe_context(), m_bit_index, m_pos, m_data[m_pos], m_inp);
				}
				m_bit_index++;

				if (m_bit_index > 8)
				{
					m_bit_index = 0;
					m_pos++;
				}
			}
			break;
		}
	}

	m_scl = state;
}

WRITE_LINE_MEMBER(pcf8583_device::sda_w)
{
	if (m_scl)
	{
		if (!state && m_sda)
		{
			// start condition (high to low when clock is high)
			m_transfer_active = true;
			m_bit_index = 0;
			m_data_recv_index = 0;
			clear_rx_buffer();
		}
		else if (state && !m_sda)
		{
			// stop condition (low to high when clock is high)
			m_transfer_active = false;
		}
	}

	m_sda = state;
}

READ_LINE_MEMBER(pcf8583_device::sda_r)
{
	return m_inp;
}

void pcf8583_device::clear_rx_buffer()
{
	m_data_recv = 0;
	m_data_recv_index = 0;
}

void pcf8583_device::set_a0(uint8_t a0)
{
	m_read_address = READ_ADDRESS_BASE | (a0 << 1);
	m_write_address = WRITE_ADDRESS_BASE | (a0 << 1);
}