mirror of
https://github.com/holub/mame
synced 2025-06-07 05:13:46 +03:00
346 lines
9.7 KiB
C++
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));
|
|
}
|