summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--src/devices/machine/v3021.cpp354
-rw-r--r--src/devices/machine/v3021.h53
2 files changed, 301 insertions, 106 deletions
diff --git a/src/devices/machine/v3021.cpp b/src/devices/machine/v3021.cpp
index a1e118b5412..0d91a9d14dc 100644
--- a/src/devices/machine/v3021.cpp
+++ b/src/devices/machine/v3021.cpp
@@ -2,13 +2,95 @@
// copyright-holders:Angelo Salese
/***************************************************************************
- v3021.c
+ v3021.cpp
- EM Microelectronic-Marin SA Ultra Low Power 32kHz CMOS RTC (DIP8)
+ EM Microelectronic-Marin SA Ultra Low Power 32kHz CMOS RTC (DIP8/SO8)
Serial Real Time Clock
- - very preliminary, borrowed from hard-coded PGM implementation.
+ - Reference: https://www.emmicroelectronic.com/product/real-time-clocks-ic/v3021
+
+ Pin assignment (SO8)
+
+ |-------------------------|
+ _|o |_
+ XI |_| |_| Vdd
+ | |
+ _| |_
+ XO |_| |_| WR
+ | V3021 |
+ _| |_
+ CS |_| |_| RD
+ | |
+ _| |_
+ Vss |_| |_| I/O
+ |-------------------------|
+
+ Pin description
+
+ |-----|------|-----------------------------------------|
+ | Pin | Name | Description |
+ |-----|------|-----------------------------------------|
+ 1 1 | XI | 32 kHz Crystal input |
+ |-----|------|-----------------------------------------|
+ 1 2 | XO | 32 kHz Crystal output |
+ |-----|------|-----------------------------------------|
+ 1 3 | CS | Chip select input |
+ |-----|------|-----------------------------------------|
+ 1 4 | Vss | Ground supply |
+ |-----|------|-----------------------------------------|
+ 1 5 | I/O | Data input and output |
+ |-----|------|-----------------------------------------|
+ 1 6 | RD | Intel RD, Motorola DS (or tie to CS) |
+ |-----|------|-----------------------------------------|
+ 1 7 | WR | Intel WR, Motorola R/W |
+ |-----|------|-----------------------------------------|
+ 1 8 | Vdd | Positive supply |
+ |-----|------|-----------------------------------------|
+
+ Register map (Unused bits are reserved)
+
+ Address Bits Description
+ 76543210
+ Data space
+ 0 ---x---- Time set lock
+ ---0---- Enable copy RAM to clock
+ ---1---- Disable copy RAM to clock
+ ----xx-- Test mode
+ ----00-- Normal operation
+ ----01-- All time keeping accelerated by 32
+ ----10-- Parallel increment of all time data
+ at 1 Hz with no carry over
+ ----11-- Parallel increment of all time data
+ at 32 Hz with no carry over
+ -------x Frequency measurement mode
+
+ 1 (Read only)
+ x------- Week number is changed
+ -x------ Weekday is changed
+ --x----- Year is changed
+ ---x---- Month is changed
+ ----x--- Day of month is changed
+ -----x-- Hours is changed
+ ------x- Minutes is changed
+ -------x Seconds is changed
+
+ 2 -xxxxxxx Seconds (BCD 00-59)
+ 3 -xxxxxxx Minutes (BCD 00-59)
+ 4 --xxxxxx Hours (BCD 00-23)
+ 5 --xxxxxx Day of month (BCD 1-31)
+ 6 ---xxxxx Month (BCD 01-12)
+ 7 xxxxxxxx Year (BCD 00-99)
+ 8 ----xxxx Week day (BCD 01-07)
+ 9 -xxxxxxx Week number (BCD 00-52)
+
+ Address command space
+ e Copy RAM to clock
+ f Copy clock to RAM
+
+ TODO:
+ - verify status bit (RAM 0x00)
+ - Support Week number correctly
***************************************************************************/
@@ -16,13 +98,12 @@
#include "machine/v3021.h"
-
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
// device type definition
-DEFINE_DEVICE_TYPE(V3021, v3021_device, "v3021", "V3021 RTC")
+DEFINE_DEVICE_TYPE(V3021, v3021_device, "v3021", "EM Microelectronic-Marin SA V3021 RTC")
//**************************************************************************
@@ -34,39 +115,26 @@ DEFINE_DEVICE_TYPE(V3021, v3021_device, "v3021", "V3021 RTC")
//-------------------------------------------------
v3021_device::v3021_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, V3021, tag, owner, clock), m_cal_mask(0), m_cal_com(0), m_cal_cnt(0), m_cal_val(0)
+ : device_t(mconfig, V3021, tag, owner, clock)
+ , device_rtc_interface(mconfig, *this)
{
}
-TIMER_CALLBACK_MEMBER(v3021_device::timer_callback)
+void v3021_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second)
{
- static constexpr uint8_t dpm[12] = { 0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31 };
- int dpm_count;
-
- m_rtc.sec++;
-
- if((m_rtc.sec & 0x0f) >= 0x0a) { m_rtc.sec+=0x10; m_rtc.sec&=0xf0; }
- if((m_rtc.sec & 0xf0) >= 0x60) { m_rtc.min++; m_rtc.sec = 0; }
- if((m_rtc.min & 0x0f) >= 0x0a) { m_rtc.min+=0x10; m_rtc.min&=0xf0; }
- if((m_rtc.min & 0xf0) >= 0x60) { m_rtc.hour++; m_rtc.min = 0; }
- if((m_rtc.hour & 0x0f) >= 0x0a) { m_rtc.hour+=0x10; m_rtc.hour&=0xf0; }
- if((m_rtc.hour & 0xff) >= 0x24) { m_rtc.day++; m_rtc.wday<<=1; m_rtc.hour = 0; }
- if(m_rtc.wday & 0x80) { m_rtc.wday = 1; }
- if((m_rtc.day & 0x0f) >= 0x0a) { m_rtc.day+=0x10; m_rtc.day&=0xf0; }
-
- /* TODO: crude leap year support */
- dpm_count = (m_rtc.month & 0xf) + (((m_rtc.month & 0x10) >> 4)*10)-1;
+ m_rtc.year = convert_to_bcd(year);
+ m_rtc.month = convert_to_bcd(month);
+ m_rtc.day = convert_to_bcd(day);
+ m_rtc.wday_bcd = convert_to_bcd(day_of_week);
+ m_rtc.wnum = convert_to_bcd((day % 7) + 1); // TODO: placeholder
+ m_rtc.hour = convert_to_bcd(hour);
+ m_rtc.min = convert_to_bcd(minute);
+ m_rtc.sec = convert_to_bcd(second);
+}
- if(((m_rtc.year % 4) == 0) && m_rtc.month == 2)
- {
- if((m_rtc.day & 0xff) >= dpm[dpm_count]+1+1)
- { m_rtc.month++; m_rtc.day = 0x01; }
- }
- else if((m_rtc.day & 0xff) >= dpm[dpm_count]+1){ m_rtc.month++; m_rtc.day = 0x01; }
- if((m_rtc.month & 0x0f) >= 0x0a) { m_rtc.month = 0x10; }
- if(m_rtc.month >= 0x13) { m_rtc.year++; m_rtc.month = 1; }
- if((m_rtc.year & 0x0f) >= 0x0a) { m_rtc.year+=0x10; m_rtc.year&=0xf0; }
- if((m_rtc.year & 0xf0) >= 0xa0) { m_rtc.year = 0; } //2000-2099 possible timeframe
+TIMER_CALLBACK_MEMBER(v3021_device::timer_callback)
+{
+ advance_seconds();
}
//-------------------------------------------------
@@ -88,16 +156,25 @@ void v3021_device::device_start()
m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(v3021_device::timer_callback), this));
m_timer->adjust(attotime::from_hz(clock() / XTAL(32'768)), 0, attotime::from_hz(clock() / XTAL(32'768)));
- system_time systime;
- machine().base_datetime(systime);
-
- m_rtc.day = ((systime.local_time.mday / 10)<<4) | ((systime.local_time.mday % 10) & 0xf);
- m_rtc.month = (((systime.local_time.month+1) / 10) << 4) | (((systime.local_time.month+1) % 10) & 0xf);
- m_rtc.wday = 1 << systime.local_time.weekday;
- m_rtc.year = (((systime.local_time.year % 100)/10)<<4) | ((systime.local_time.year % 10) & 0xf);
- m_rtc.hour = ((systime.local_time.hour / 10)<<4) | ((systime.local_time.hour % 10) & 0xf);
- m_rtc.min = ((systime.local_time.minute / 10)<<4) | ((systime.local_time.minute % 10) & 0xf);
- m_rtc.sec = ((systime.local_time.second / 10)<<4) | ((systime.local_time.second % 10) & 0xf);
+ copy_clock_to_ram();
+
+ save_item(NAME(m_cs));
+ save_item(NAME(m_io));
+ save_item(NAME(m_addr));
+ save_item(NAME(m_data));
+ save_item(NAME(m_ram));
+ save_item(NAME(m_cnt));
+ save_item(NAME(m_mode));
+
+ save_item(NAME(m_rtc.sec));
+ save_item(NAME(m_rtc.min));
+ save_item(NAME(m_rtc.hour));
+ save_item(NAME(m_rtc.day));
+ save_item(NAME(m_rtc.wday));
+ save_item(NAME(m_rtc.wday_bcd));
+ save_item(NAME(m_rtc.wnum));
+ save_item(NAME(m_rtc.month));
+ save_item(NAME(m_rtc.year));
}
@@ -107,75 +184,162 @@ void v3021_device::device_start()
void v3021_device::device_reset()
{
- m_cal_cnt = 0;
+ m_cs = false;
+ m_io = 0;
+ m_addr = 0;
+ m_data = 0;
+ m_ram[0] = m_ram[1] = 0;
+ m_cnt = 0;
+ m_mode = 0;
}
-//**************************************************************************
-// READ/WRITE HANDLERS
-//**************************************************************************
+//-------------------------------------------------
+// read/write - parallel interface, verified from
+// "typical applications" section from datasheet
+//-------------------------------------------------
-uint8_t v3021_device::read()
+u8 v3021_device::read()
{
- uint8_t calr = (m_cal_val & m_cal_mask) ? 1 : 0;
-
- m_cal_mask <<= 1;
- return calr;
+ u8 ret = io_r();
+ if (!machine().side_effects_disabled())
+ {
+ cs_w(1);
+ cs_w(0);
+ }
+ return ret & 1;
}
-void v3021_device::write(uint8_t data)
+void v3021_device::write(u8 data)
{
- m_cal_com <<= 1;
- m_cal_com |= data & 1;
- ++m_cal_cnt;
-
- if (m_cal_cnt == 4)
+ io_w(BIT(data, 0));
+ if (!machine().side_effects_disabled())
{
- m_cal_mask = 1;
- m_cal_val = 1;
- m_cal_cnt = 0;
+ cs_w(1);
+ cs_w(0);
+ }
+}
- switch (m_cal_com & 0xf)
- {
- case 1: case 3: case 5: case 7: case 9: case 0xb: case 0xd:
- m_cal_val++;
- break;
+//-------------------------------------------------
+// cs_w - CS pin handler
+//-------------------------------------------------
- case 0:
- m_cal_val = (m_rtc.wday); //??
- break;
+WRITE_LINE_MEMBER(v3021_device::cs_w)
+{
+ if (m_cs != state)
+ {
+ m_cs = state;
+ if (m_cs)
+ {
+ if (!m_mode) // address
+ {
+ m_addr = ((m_addr >> 1) | (m_io << 3)) & 0xf;
+ if (++m_cnt >= 4)
+ {
+ if (m_addr == 0xe) // copy RAM to clock
+ {
+ if (!BIT(m_ram[0x0], 4)) // time set lock
+ copy_ram_to_clock();
+ }
+ else if (m_addr == 0xf) // copy clock to RAM
+ {
+ copy_clock_to_ram();
+ }
+ else // normal accessing
+ {
+ m_data = m_ram[m_addr & 0xf];
+ m_mode = 1;
+ }
+ m_cnt = 0;
+ }
+ }
+ else // data
+ {
+ m_data = ((m_data >> 1) | (m_io << 7)) & 0xff;
+ if (++m_cnt >= 8)
+ {
+ if (m_addr != 1 && m_addr <= 9)
+ m_ram[m_addr & 0xf] = m_data;
+ else
+ logerror("%s: Writing undocumented register %02X at %02X\n", this->tag(), m_data, m_addr);
+
+ m_mode = 0;
+ m_cnt = 0;
+ }
+ }
+ }
+ }
+}
- case 2: //Hours
- m_cal_val = (m_rtc.hour);
- break;
+//-------------------------------------------------
+// io_w - I/O pin write handler
+//-------------------------------------------------
- case 4: //Seconds
- m_cal_val = (m_rtc.sec);
- break;
+WRITE_LINE_MEMBER(v3021_device::io_w)
+{
+ m_io = state;
+}
- case 6: //Month
- m_cal_val = (m_rtc.month); //?? not bcd in MVS
- break;
+//-------------------------------------------------
+// io_r - I/O pin read handler
+//-------------------------------------------------
- case 8:
- m_cal_val = 0; //Controls blinking speed, maybe milliseconds
- break;
+READ_LINE_MEMBER(v3021_device::io_r)
+{
+ return m_data & 1;
+}
- case 0xa: //Day
- m_cal_val = (m_rtc.day);
- break;
+//-------------------------------------------------
+// copy_clock_to_ram - copy current clock to RAM
+//-------------------------------------------------
- case 0xc: //Minute
- m_cal_val = (m_rtc.min);
- break;
+void v3021_device::copy_clock_to_ram()
+{
+ // set status1
+ m_ram[0x1] = 0;
+ if (m_ram[0x2] != m_rtc.sec)
+ m_ram[0x1] |= (1 << 0);
+ if (m_ram[0x3] != m_rtc.min)
+ m_ram[0x1] |= (1 << 1);
+ if (m_ram[0x4] != m_rtc.hour)
+ m_ram[0x1] |= (1 << 2);
+ if (m_ram[0x5] != m_rtc.day)
+ m_ram[0x1] |= (1 << 3);
+ if (m_ram[0x6] != m_rtc.month)
+ m_ram[0x1] |= (1 << 4);
+ if (m_ram[0x7] != m_rtc.year)
+ m_ram[0x1] |= (1 << 5);
+ if (m_ram[0x8] != m_rtc.wday_bcd)
+ m_ram[0x1] |= (1 << 6);
+ if (m_ram[0x9] != m_rtc.wnum)
+ m_ram[0x1] |= (1 << 7);
+
+ m_ram[0x2] = m_rtc.sec;
+ m_ram[0x3] = m_rtc.min;
+ m_ram[0x4] = m_rtc.hour;
+ m_ram[0x5] = m_rtc.day;
+ m_ram[0x6] = m_rtc.month;
+ m_ram[0x7] = m_rtc.year;
+ m_ram[0x8] = m_rtc.wday_bcd;
+ m_ram[0x9] = m_rtc.wnum;
+}
- case 0xe: //Year
- m_cal_val = (m_rtc.year % 100);
- break;
+//-------------------------------------------------
+// copy_ram_to_clock - copy RAM data to clock
+//-------------------------------------------------
- case 0xf: //Load Date
- //machine().base_datetime(m_systime);
- break;
- }
- }
+void v3021_device::copy_ram_to_clock()
+{
+ // clear status1
+ m_ram[0x1] = 0;
+ m_rtc.sec = m_ram[0x2];
+ m_rtc.min = m_ram[0x3];
+ m_rtc.hour = m_ram[0x4];
+ m_rtc.day = m_ram[0x5];
+ m_rtc.month = m_ram[0x6];
+ m_rtc.year = m_ram[0x7];
+ m_rtc.wday_bcd = m_ram[0x8];
+ m_rtc.wnum = m_ram[0x9];
+ set_time(true, bcd_to_integer(m_rtc.year), bcd_to_integer(m_rtc.month), bcd_to_integer(m_rtc.day),
+ bcd_to_integer(m_rtc.wday_bcd), bcd_to_integer(m_rtc.hour), bcd_to_integer(m_rtc.min), bcd_to_integer(m_rtc.sec));
}
diff --git a/src/devices/machine/v3021.h b/src/devices/machine/v3021.h
index cbbe8e3f0e6..298dd4134f6 100644
--- a/src/devices/machine/v3021.h
+++ b/src/devices/machine/v3021.h
@@ -4,7 +4,7 @@
v3021.h
- EM Microelectronic-Marin SA Ultra Low Power 32kHz CMOS RTC (DIP8)
+ EM Microelectronic-Marin SA Ultra Low Power 32kHz CMOS RTC (DIP8/SO8)
Serial Real Time Clock
@@ -15,6 +15,7 @@
#pragma once
+#include "dirtc.h"
//**************************************************************************
// TYPE DEFINITIONS
@@ -22,32 +23,62 @@
// ======================> v3021_device
-class v3021_device : public device_t
+class v3021_device : public device_t, public device_rtc_interface
{
public:
// construction/destruction
v3021_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 32'768);
- // I/O operations
- void write(uint8_t data);
- uint8_t read();
+ // parallel interface
+ void write(u8 data);
+ u8 read();
+
+ // serial interface
+ DECLARE_WRITE_LINE_MEMBER(cs_w);
+ DECLARE_WRITE_LINE_MEMBER(io_w);
+ DECLARE_READ_LINE_MEMBER(io_r);
+
TIMER_CALLBACK_MEMBER(timer_callback);
protected:
- struct rtc_regs_t
- {
- uint8_t sec, min, hour, day, wday, month, year;
- };
-
// device-level overrides
virtual void device_validity_check(validity_checker &valid) const override;
virtual void device_start() override;
virtual void device_reset() override;
- uint8_t m_cal_mask,m_cal_com,m_cal_cnt,m_cal_val;
+ // device_rtc_interface overrides
+ virtual bool rtc_feature_y2k() const override { return false; }
+ virtual bool rtc_feature_leap_year() const override { return true; }
+ virtual bool rtc_battery_backed() const override { return true; }
+ virtual void rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) override;
+
+private:
+ struct rtc_regs_t
+ {
+ u8 sec = 0;
+ u8 min = 0;
+ u8 hour = 0;
+ u8 day = 1;
+ u8 wday = 1;
+ u8 wday_bcd = 1;
+ u8 wnum = 0;
+ u8 month = 1;
+ u8 year = 0;
+ };
+
+ bool m_cs = false;
+ bool m_io = false;
+ u8 m_addr = 0;
+ u8 m_data = 0;
+ u8 m_ram[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ u8 m_cnt = 0;
+ u8 m_mode = 0;
rtc_regs_t m_rtc;
+ void copy_clock_to_ram();
+ void copy_ram_to_clock();
+
emu_timer *m_timer;
};