mame/src/devices/machine/v3021.cpp
2020-07-26 12:56:13 +10:00

346 lines
9.7 KiB
C++

// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/***************************************************************************
v3021.cpp
EM Microelectronic-Marin SA Ultra Low Power 32kHz CMOS RTC (DIP8/SO8)
Serial Real Time Clock
- 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
***************************************************************************/
#include "emu.h"
#include "machine/v3021.h"
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
// device type definition
DEFINE_DEVICE_TYPE(V3021, v3021_device, "v3021", "EM Microelectronic-Marin SA V3021 RTC")
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// v3021_device - constructor
//-------------------------------------------------
v3021_device::v3021_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, V3021, tag, owner, clock)
, device_rtc_interface(mconfig, *this)
{
}
void v3021_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second)
{
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);
}
TIMER_CALLBACK_MEMBER(v3021_device::timer_callback)
{
advance_seconds();
}
//-------------------------------------------------
// device_validity_check - perform validity checks
// on this device
//-------------------------------------------------
void v3021_device::device_validity_check(validity_checker &valid) const
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void v3021_device::device_start()
{
/* let's call the timer callback every second */
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)));
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));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void v3021_device::device_reset()
{
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 - parallel interface, verified from
// "typical applications" section from datasheet
//-------------------------------------------------
u8 v3021_device::read()
{
u8 ret = io_r();
if (!machine().side_effects_disabled())
{
cs_w(1);
cs_w(0);
}
return ret & 1;
}
void v3021_device::write(u8 data)
{
io_w(BIT(data, 0));
if (!machine().side_effects_disabled())
{
cs_w(1);
cs_w(0);
}
}
//-------------------------------------------------
// cs_w - CS pin handler
//-------------------------------------------------
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;
}
}
}
}
}
//-------------------------------------------------
// io_w - I/O pin write handler
//-------------------------------------------------
WRITE_LINE_MEMBER(v3021_device::io_w)
{
m_io = state;
}
//-------------------------------------------------
// io_r - I/O pin read handler
//-------------------------------------------------
READ_LINE_MEMBER(v3021_device::io_r)
{
return m_data & 1;
}
//-------------------------------------------------
// copy_clock_to_ram - copy current clock to RAM
//-------------------------------------------------
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;
}
//-------------------------------------------------
// copy_ram_to_clock - copy RAM data to clock
//-------------------------------------------------
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));
}