// license:BSD-3-Clause
// copyright-holders:AJR
/**********************************************************************
Dallas DS17x85/DS17x87 Real Time Clocks with extended NVSRAM
These MC146818-compatible RTCs overlay the last 64 bytes of user
RAM (not provided by the MC146818) with a second page of registers,
including a Y2K-updated century count and registers for indirectly
accessing extended SRAM which is backed with a separate auxiliary
battery power input.
The DS1685 and DS17x85 use conventional PDIP/SO/TSOP packages,
while the DS1687 and DS17x87 integrate battery, oscillator and chip
into one of Dallas's trademark EDIPs. They are indistinguishable
from the perspective of software.
Currently unemulated features include:
- Kickstart input
- Date alarm wakeup
- Increment in progress flag
- RAM clear function (software-enabled, hardware-triggered)
- 32.768 kHz frequency option for SQW output
**********************************************************************/
#include "emu.h"
#include "ds17x85.h"
#define VERBOSE 0
#include "logmacro.h"
// device type definitions
DEFINE_DEVICE_TYPE(DS1685, ds1685_device, "ds1685", "DS1685 RTC")
DEFINE_DEVICE_TYPE(DS1687, ds1687_device, "ds1687", "DS1687 RTC")
DEFINE_DEVICE_TYPE(DS17285, ds17285_device, "ds17285", "DS17285 RTC")
DEFINE_DEVICE_TYPE(DS17287, ds17287_device, "ds17287", "DS17287 RTC")
DEFINE_DEVICE_TYPE(DS17485, ds17485_device, "ds17485", "DS17485 RTC")
DEFINE_DEVICE_TYPE(DS17487, ds17487_device, "ds17487", "DS17487 RTC")
DEFINE_DEVICE_TYPE(DS17885, ds17885_device, "ds17885", "DS17885 RTC")
DEFINE_DEVICE_TYPE(DS17887, ds17887_device, "ds17887", "DS17887 RTC")
//-------------------------------------------------
// ds17x85_device - constructor
//-------------------------------------------------
ds17x85_device::ds17x85_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u8 model, u32 extram_size)
: mc146818_device(mconfig, type, tag, owner, clock)
, m_model(model)
, m_extram_size(extram_size)
, m_smi_stack1(0)
{
m_century_index = REG_EXT_CENTURY;
}
//-------------------------------------------------
// ds1685_device - constructor
//-------------------------------------------------
ds1685_device::ds1685_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: ds17x85_device(mconfig, DS1685, tag, owner, clock, 0x71, 128)
{
}
//-------------------------------------------------
// ds1687_device - constructor
//-------------------------------------------------
ds1687_device::ds1687_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: ds17x85_device(mconfig, DS1687, tag, owner, clock, 0x71, 128)
{
}
//-------------------------------------------------
// ds17285_device - constructor
//-------------------------------------------------
ds17285_device::ds17285_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: ds17x85_device(mconfig, DS17285, tag, owner, clock, 0x72, 2048)
{
}
//-------------------------------------------------
// ds17287_device - constructor
//-------------------------------------------------
ds17287_device::ds17287_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: ds17x85_device(mconfig, DS17287, tag, owner, clock, 0x72, 2048)
{
}
//-------------------------------------------------
// ds17485_device - constructor
//-------------------------------------------------
ds17485_device::ds17485_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: ds17x85_device(mconfig, DS17485, tag, owner, clock, 0x74, 4096)
{
}
//-------------------------------------------------
// ds17487_device - constructor
//-------------------------------------------------
ds17487_device::ds17487_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: ds17x85_device(mconfig, DS17487, tag, owner, clock, 0x74, 4096)
{
}
//-------------------------------------------------
// ds17885_device - constructor
//-------------------------------------------------
ds17885_device::ds17885_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: ds17x85_device(mconfig, DS17885, tag, owner, clock, 0x78, 8192)
{
}
//-------------------------------------------------
// ds17887_device - constructor
//-------------------------------------------------
ds17887_device::ds17887_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: ds17x85_device(mconfig, DS17887, tag, owner, clock, 0x78, 8192)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void ds17x85_device::device_start()
{
mc146818_device::device_start();
save_item(NAME(m_smi_stack1));
}
//-------------------------------------------------
// nvram_default - called to initialize NVRAM to
// its default state
//-------------------------------------------------
void ds17x85_device::nvram_default()
{
mc146818_device::nvram_default();
if (!m_region.found())
{
// initialize read-only model number and 64-bit serial number
m_data[REG_EXT_MODEL] = m_model;
m_data[REG_EXT_SERIAL1] = 0x4d;
m_data[REG_EXT_SERIAL2] = 0x41;
m_data[REG_EXT_SERIAL3] = 0x4d;
m_data[REG_EXT_SERIAL4] = 0x45;
m_data[REG_EXT_SERIAL5] = 0x64;
m_data[REG_EXT_SERIAL6] = 0x76;
m_data[REG_EXT_SERIAL_CRC] = 0xf8;
}
}
//---------------------------------------------------------------
// get_timer_bypass - get main clock divisor based on A register
//---------------------------------------------------------------
int ds17x85_device::get_timer_bypass() const
{
int bypass;
// DV0 is used for bank switching only
switch (m_data[REG_A] & (REG_A_DV2 | REG_A_DV1))
{
case REG_A_DV1:
bypass = 7;
break;
case REG_A_DV2 | REG_A_DV1:
bypass = 22;
break;
default:
// TODO: other combinations of divider bits disable the oscillator
bypass = 22;
break;
}
return bypass;
}
//---------------------------------------------------------------
// internal_set_address - latch address on ALE
//---------------------------------------------------------------
void ds17x85_device::internal_set_address(uint8_t address)
{
// push previous address onto SMI recovery stack
m_data[REG_EXT_SMI_STACK3] = m_data[REG_EXT_SMI_STACK2];
m_data[REG_EXT_SMI_STACK2] = m_smi_stack1;
m_smi_stack1 = m_index | ((m_data[REG_A] & REG_A_DV0) ? 0x80 : 0x00);
mc146818_device::internal_set_address(address);
}
//---------------------------------------------------------------
// internal_read - read internal data
//---------------------------------------------------------------
uint8_t ds17x85_device::internal_read(offs_t offset)
{
if (offset >= 0x40 && (m_data[REG_A] & REG_A_DV0))
{
offset += 0x40;
switch (offset)
{
case REG_EXT_4A:
return m_data[offset] | REG_EXT_4A_VRT2;
case REG_EXT_RAM_DATA:
LOG("%s: Reading from extended RAM at %04X\n", machine().describe_context(),
m_data[REG_EXT_RAM_ADDRL] | offs_t(m_data[REG_EXT_RAM_ADDRH]) << 8);
offset = REG_EXT_RAM_BASE + (m_data[REG_EXT_RAM_ADDRL] | offs_t(m_data[REG_EXT_RAM_ADDRH]) << 8) % m_extram_size;
if ((m_data[REG_EXT_4A] & REG_EXT_4A_BME) && !machine().side_effects_disabled())
{
m_data[REG_EXT_RAM_ADDRL]++;
if (m_data[REG_EXT_RAM_ADDRL] == 0 && m_extram_size > 256)
m_data[REG_EXT_RAM_ADDRH]++;
}
break;
default:
break;
}
}
return mc146818_device::internal_read(offset);
}
//---------------------------------------------------------------
// internal_write - write internal data
//---------------------------------------------------------------
void ds17x85_device::internal_write(offs_t offset, uint8_t data)
{
if (!machine().side_effects_disabled())
m_data[REG_EXT_WRITE_COUNTER]++;
if (offset >= 0x40 && (m_data[REG_A] & REG_A_DV0))
{
offset += 0x40;
switch (offset)
{
case REG_EXT_RAM_DATA:
LOG("%s: Writing %02X to extended RAM at %04X\n", machine().describe_context(),
data, m_data[REG_EXT_RAM_ADDRL] | offs_t(m_data[REG_EXT_RAM_ADDRH]) << 8);
offset = REG_EXT_RAM_BASE + (m_data[REG_EXT_RAM_ADDRL] | offs_t(m_data[REG_EXT_RAM_ADDRH]) << 8) % m_extram_size;
if ((m_data[REG_EXT_4A] & REG_EXT_4A_BME) && !machine().side_effects_disabled())
{
m_data[REG_EXT_RAM_ADDRL]++;
if (m_data[REG_EXT_RAM_ADDRL] == 0 && m_extram_size > 256)
m_data[REG_EXT_RAM_ADDRH]++;
}
break;
case REG_EXT_CENTURY:
case REG_EXT_DATE_ALARM: // TODO: unimplemented
case REG_EXT_4A: // TODO: mostly unimplemented
case REG_EXT_4B: // TODO: unimplemented
case REG_EXT_RAM_ADDRL:
break;
case REG_EXT_RAM_ADDRH:
if (m_extram_size <= 256)
{
logerror("%s: RTC extended addressing uses one byte only\n", machine().describe_context());
return;
}
break;
default:
logerror("%s: Write to reserved/read-only bank 1 register %02X\n", machine().describe_context(), offset - 0x40);
return;
}
}
mc146818_device::internal_write(offset, data);
}