diff options
Diffstat (limited to 'src/devices/machine/pcf8573.cpp')
-rw-r--r-- | src/devices/machine/pcf8573.cpp | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/src/devices/machine/pcf8573.cpp b/src/devices/machine/pcf8573.cpp new file mode 100644 index 00000000000..a0d12344db6 --- /dev/null +++ b/src/devices/machine/pcf8573.cpp @@ -0,0 +1,356 @@ +// 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; +} |