diff options
Diffstat (limited to 'src/devices/machine/pcf8583.cpp')
-rw-r--r-- | src/devices/machine/pcf8583.cpp | 437 |
1 files changed, 311 insertions, 126 deletions
diff --git a/src/devices/machine/pcf8583.cpp b/src/devices/machine/pcf8583.cpp index 47babd1a2ac..40c9d201a88 100644 --- a/src/devices/machine/pcf8583.cpp +++ b/src/devices/machine/pcf8583.cpp @@ -1,77 +1,80 @@ // license:BSD-3-Clause -// copyright-holders:Tim Schuerewegen, Ryan Holtz +// copyright-holders:Nigel Barnes /********************************************************************* Philips PCF8583 Clock and Calendar with 240 x 8-bit RAM TODO: - - Alarm mode - - Event-counter mode - - Clock select - - Clock output - - Interrupts + - Event-counter mode *********************************************************************/ #include "emu.h" #include "pcf8583.h" +#define LOG_DATA (1U << 1) +#define LOG_LINE (1U << 2) + + +#define VERBOSE (0) +#include "logmacro.h" + + DEFINE_DEVICE_TYPE(PCF8583, pcf8583_device, "pcf8583", "PCF8583 RTC with 240x8 RAM") +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// pcf8583_device - constructor +//------------------------------------------------- + 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) + , m_region(*this, DEVICE_SELF) + , m_irq_cb(*this) + , m_slave_address(PCF8583_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_register(0) + , m_timer(nullptr) { } +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + void pcf8583_device::device_start() { - std::fill(std::begin(m_register), std::end(m_register), 0); - - m_timer = timer_alloc(TIMER_TICK); + m_timer = timer_alloc(FUNC(pcf8583_device::clock_tick), this); 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_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_register)); 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()); + save_item(NAME(m_data)); + save_item(NAME(m_slave_address)); } -void pcf8583_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +TIMER_CALLBACK_MEMBER(pcf8583_device::clock_tick) { - switch(id) - { - case TIMER_TICK: - if (!BIT(m_data[REG_CONTROL], CONTROL_STOP_BIT)) - advance_hundredths(); - break; - } + if (!BIT(m_data[REG_CONTROL], CONTROL_STOP_BIT)) + advance_hundredths(); } void pcf8583_device::advance_hundredths() @@ -83,8 +86,8 @@ void pcf8583_device::advance_hundredths() hundredths = 0; advance_seconds(); m_irq = !m_irq; - printf("Toggling IRQ: %d\n", m_irq ? 1 : 0); - m_irq_callback(m_irq); + + m_irq_cb(m_irq); } m_data[REG_HUNDREDTHS] = convert_to_bcd(hundredths); } @@ -98,138 +101,320 @@ void pcf8583_device::rtc_clock_updated(int year, int month, int day, int day_of_ set_date_day(day); set_date_month(month); set_date_year(year); + + if (BIT(m_data[REG_HOURS], 7)) // 12h format + { + // update AM/PM flag + m_data[REG_HOURS] = (m_data[REG_HOURS] & 0xbf) | (bcd_to_integer(m_data[REG_HOURS] & 0x3f) >= 12 ? 0x40 : 0x00); + // convert from 24h to 12h + m_data[REG_HOURS] = (m_data[REG_HOURS] & 0xc0) | convert_to_bcd(bcd_to_integer((m_data[REG_HOURS] & 0x3f) % 12)); + } + + if (BIT(m_data[REG_CONTROL], 2)) // alarm enabled + { + switch (m_data[REG_ALARM_CONTROL] & 0x30) + { + case 0x00: // no alarm + break; + + case 0x10: // daily alarm + if (m_data[REG_HUNDREDTHS] == m_data[REG_ALARM_HUNDREDTHS] && m_data[REG_SECONDS] == m_data[REG_ALARM_SECONDS] && + m_data[REG_MINUTES] == m_data[REG_ALARM_MINUTES] && m_data[REG_HOURS] == m_data[REG_ALARM_HOURS]) + { + m_data[REG_ALARM_CONTROL] |= 0x80; + } + break; + + case 0x20: // weekday alarm + if (BIT(m_data[REG_ALARM_MONTH], m_data[REG_MONTH_DAY] >> 5)) // weekday enabled + { + if (m_data[REG_HUNDREDTHS] == m_data[REG_ALARM_HUNDREDTHS] && m_data[REG_SECONDS] == m_data[REG_ALARM_SECONDS] && + m_data[REG_MINUTES] == m_data[REG_ALARM_MINUTES] && m_data[REG_HOURS] == m_data[REG_ALARM_HOURS]) + { + m_data[REG_ALARM_CONTROL] |= 0x80; + } + } + break; + + case 0x30: // dated alarm + if (m_data[REG_HUNDREDTHS] == m_data[REG_ALARM_HUNDREDTHS] && m_data[REG_SECONDS] == m_data[REG_ALARM_SECONDS] && + m_data[REG_MINUTES] == m_data[REG_ALARM_MINUTES] && m_data[REG_HOURS] == m_data[REG_ALARM_HOURS] && + (m_data[REG_YEAR_DATE] & 0x3f) == m_data[REG_ALARM_DATE] && (m_data[REG_MONTH_DAY] & 0x1f) == m_data[REG_ALARM_MONTH]) + { + m_data[REG_ALARM_CONTROL] |= 0x80; + } + break; + } + + // alarm interrupt enable + m_irq_cb(BIT(m_data[REG_ALARM_CONTROL], 7)); + } } +//------------------------------------------------- +// nvram_default - called to initialize NVRAM to +// its default state +//------------------------------------------------- + void pcf8583_device::nvram_default() { - std::fill(std::begin(m_data), std::end(m_data), 0); + // populate from a memory region if present + if (m_region.found()) + { + if (m_region->bytes() != 0x100) + { + fatalerror("pcf8583 region '%s' wrong size (expected size = 0x100)\n", tag()); + } + + std::copy_n(m_region->base(), m_region->bytes(), &m_data[0]); + } + else + { + std::fill(std::begin(m_data), std::end(m_data), 0); + } } -void pcf8583_device::nvram_read(emu_file &file) +//------------------------------------------------- +// nvram_read - called to read NVRAM from the +// .nv file +//------------------------------------------------- + +bool pcf8583_device::nvram_read(util::read_stream &file) { - file.read(m_data, sizeof(m_data)); + auto const [err, actual] = read(file, m_data, sizeof(m_data)); + return !err && (actual == sizeof(m_data)); } -void pcf8583_device::nvram_write(emu_file &file) +//------------------------------------------------- +// nvram_write - called to write NVRAM to the +// .nv file +//------------------------------------------------- + +bool pcf8583_device::nvram_write(util::write_stream &file) { - file.write(m_data, sizeof(m_data)); + auto const [err, actual] = write(file, m_data, sizeof(m_data)); + return !err; } -void pcf8583_device::write_register(uint8_t offset, uint8_t data) + +//************************************************************************** +// READ/WRITE HANDLERS +//************************************************************************** + +void pcf8583_device::a0_w(int state) { - logerror("%s: write_register: address %02x = %02x\n", machine().describe_context(), offset, data); - m_data[offset] = data; + 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); + } } -WRITE_LINE_MEMBER(pcf8583_device::scl_w) +void pcf8583_device::scl_w(int state) { - if (m_transfer_active && !m_scl && state) + if (m_scl != state) { - switch (m_mode) + m_scl = state; + LOGMASKED(LOG_LINE, "set_scl_line %d\n", m_scl); + + switch (m_state) { - case RTC_MODE_RECV: + case STATE_DEVSEL: + case STATE_REGISTER: + case STATE_DATAIN: + if (m_bits < 8) { - 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_scl) + { + m_shift = ((m_shift << 1) | m_sdaw) & 0xff; + m_bits++; + } + } + else + { + if (m_scl) + { + m_bits++; + } + else { - if (m_data_recv_index == 0) + if( m_bits == 8 ) { - if (m_data_recv == m_read_address) + switch (m_state) { - 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; + case STATE_DEVSEL: + m_devsel = m_shift; + + if ((m_devsel & 0xfe) != m_slave_address) + { + LOGMASKED(LOG_DATA, "devsel %02x: not this device\n", m_devsel); + m_state = STATE_IDLE; + } + else if ((m_devsel & 1) == 0) + { + LOGMASKED(LOG_DATA, "devsel %02x: write\n", m_devsel); + m_state = STATE_REGISTER; + } + else + { + LOGMASKED(LOG_DATA, "devsel %02x: read\n", m_devsel); + m_state = STATE_READSELACK; + } + break; + + case STATE_REGISTER: + m_register = m_shift; + + LOGMASKED(LOG_DATA, "register %02x\n", m_register); + + m_state = STATE_DATAIN; + break; + + case STATE_DATAIN: + LOGMASKED(LOG_DATA, "data[ %02x ] <- %02x\n", m_register, m_shift); + + m_data[m_register] = m_shift; + + switch (m_register) + { + case REG_CONTROL: + if ((m_shift & 0x24) == 0x04) + logerror("Timer not implemented"); + break; + case REG_SECONDS: + set_clock_register(RTC_SECOND, bcd_to_integer(m_data[REG_SECONDS])); + break; + case REG_MINUTES: + set_clock_register(RTC_MINUTE, bcd_to_integer(m_data[REG_MINUTES])); + break; + case REG_HOURS: + set_clock_register(RTC_HOUR, bcd_to_integer(m_data[REG_HOURS])); + break; + case REG_YEAR_DATE: + set_clock_register(RTC_DAY, bcd_to_integer(m_data[REG_YEAR_DATE] & 0x3f)); + set_clock_register(RTC_YEAR, bcd_to_integer(m_data[REG_YEAR_DATE] >> 6)); + break; + case REG_MONTH_DAY: + set_clock_register(RTC_MONTH, bcd_to_integer(m_data[REG_MONTH_DAY] & 0x1f)); + set_clock_register(RTC_DAY_OF_WEEK, bcd_to_integer((m_data[REG_MONTH_DAY] >> 5) + 1)); + break; + case REG_ALARM_CONTROL: + m_irq_cb(m_data[REG_ALARM_CONTROL] & 0x88 ? 1 : 0); + break; + } + + m_register++; + break; } - else + + if (m_state != STATE_IDLE) { - logerror("%s: scl_w: Received byte 0 (%02x), unknown address, going idle\n", machine().describe_context(), m_data_recv); - m_mode = RTC_MODE_NONE; + m_sdar = 0 ; } } - else if (m_data_recv_index == 1) + else { - 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; + m_bits = 0; + m_sdar = 1; } - 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: + case STATE_READSELACK: + m_bits = 0; + m_state = STATE_DATAOUT; + break; + + case STATE_DATAOUT: + if (m_bits < 8) { - if (m_bit_index < 8) + if (m_scl) { - 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_bits++; } - m_bit_index++; + else + { + if (m_bits == 0) + { + m_shift = m_data[m_register]; - if (m_bit_index > 8) + switch (m_register) + { + case 0x05: + if (BIT(m_data[0x00], 3)) // mask flag + m_shift &= 0x3f; + break; + case 0x06: + if (BIT(m_data[0x00], 3)) // mask flag + m_shift &= 0x1f; + break; + } + + LOGMASKED(LOG_DATA, "data[ %02x ] -> %02x\n", m_register, m_shift); + m_register++; + } + + m_sdar = (m_shift >> 7) & 1; + + m_shift = (m_shift << 1) & 0xff; + } + } + else + { + if (m_scl) { - m_bit_index = 0; - m_pos++; + if (m_sdaw) + { + LOGMASKED(LOG_DATA, "nack\n"); + m_state = STATE_IDLE; + } + + m_bits = 0; + } + else + { + m_sdar = 1; } } break; } } - - m_scl = state; } -WRITE_LINE_MEMBER(pcf8583_device::sda_w) +void pcf8583_device::sda_w(int state) { - if (m_scl) + state &= 1; + if (m_sdaw != state) { - 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) + LOGMASKED(LOG_LINE, "set sda %d\n", state); + m_sdaw = state; + + if (m_scl) { - // stop condition (low to high when clock is high) - m_transfer_active = false; + if (m_sdaw) + { + LOGMASKED(LOG_DATA, "stop\n"); + m_state = STATE_IDLE; + } + else + { + LOGMASKED(LOG_DATA, "start\n"); + m_state = STATE_DEVSEL; + m_bits = 0; + } + + m_sdar = 1; } } - - m_sda = state; } -READ_LINE_MEMBER(pcf8583_device::sda_r) +int pcf8583_device::sda_r() { - return m_inp; -} + int res = m_sdar & 1; -void pcf8583_device::clear_rx_buffer() -{ - m_data_recv = 0; - m_data_recv_index = 0; -} + LOGMASKED(LOG_LINE, "read sda %d\n", res); -void pcf8583_device::set_a0(uint8_t a0) -{ - m_read_address = READ_ADDRESS_BASE | (a0 << 1); - m_write_address = WRITE_ADDRESS_BASE | (a0 << 1); + return res; } |