mirror of
https://github.com/holub/mame
synced 2025-05-28 08:33:05 +03:00
485 lines
13 KiB
C++
485 lines
13 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Aaron Giles,smf
|
|
/***************************************************************************
|
|
|
|
timekpr.h
|
|
|
|
Various ST Microelectronics timekeeper SRAM implementations:
|
|
- M48T02
|
|
- M48T35
|
|
- M48T37
|
|
- M48T58
|
|
- MK48T08
|
|
- MK48T12
|
|
|
|
***************************************************************************/
|
|
|
|
#include "emu.h"
|
|
#include "machine/timekpr.h"
|
|
#include "machine/timehelp.h"
|
|
|
|
#define LOG_GENERAL (1U << 0)
|
|
#define LOG_TICKS (1U << 1)
|
|
|
|
#define VERBOSE (0)
|
|
#include "logmacro.h"
|
|
|
|
// device type definition
|
|
DEFINE_DEVICE_TYPE(M48T02, m48t02_device, "m48t02", "M48T02 Timekeeper")
|
|
DEFINE_DEVICE_TYPE(M48T35, m48t35_device, "m48t35", "M48T35 Timekeeper")
|
|
DEFINE_DEVICE_TYPE(M48T37, m48t37_device, "m48t37", "M48T37 Timekeeper")
|
|
DEFINE_DEVICE_TYPE(M48T58, m48t58_device, "m48t58", "M48T58 Timekeeper")
|
|
DEFINE_DEVICE_TYPE(MK48T08, mk48t08_device, "mk48t08", "MK48T08 Timekeeper")
|
|
DEFINE_DEVICE_TYPE(MK48T12, mk48t12_device, "mk48t12", "MK48T12 Timekeeper")
|
|
|
|
|
|
/***************************************************************************
|
|
MACROS
|
|
***************************************************************************/
|
|
|
|
#define MASK_SECONDS ( 0x7f )
|
|
#define MASK_MINUTES ( 0x7f )
|
|
#define MASK_HOURS ( 0x3f )
|
|
#define MASK_DAY ( 0x07 )
|
|
#define MASK_DATE ( 0x3f )
|
|
#define MASK_MONTH ( 0x1f )
|
|
#define MASK_YEAR ( 0xff )
|
|
#define MASK_CENTURY ( 0xff )
|
|
|
|
#define CONTROL_W ( 0x80 )
|
|
#define CONTROL_R ( 0x40 )
|
|
#define CONTROL_S ( 0x20 ) /* not emulated */
|
|
#define CONTROL_CALIBRATION ( 0x1f ) /* not emulated */
|
|
|
|
#define SECONDS_ST ( 0x80 )
|
|
|
|
#define DAY_FT ( 0x40 ) /* M48T37 - not emulated */
|
|
#define DAY_CEB ( 0x20 ) /* M48T35/M48T58 */
|
|
#define DAY_CB ( 0x10 ) /* M48T35/M48T58 */
|
|
|
|
#define DATE_BLE ( 0x80 ) /* M48T58: not emulated */
|
|
#define DATE_BL ( 0x40 ) /* M48T58: not emulated */
|
|
|
|
#define FLAGS_BL ( 0x10 ) /* MK48T08/M48T37: not emulated */
|
|
#define FLAGS_AF ( 0x40 ) /* M48T37: not emulated */
|
|
#define FLAGS_WDF ( 0x80 ) /* M48T37 */
|
|
|
|
|
|
/***************************************************************************
|
|
INLINE FUNCTIONS
|
|
***************************************************************************/
|
|
|
|
static void counter_to_ram( uint8_t *data, int offset, int counter )
|
|
{
|
|
if( offset >= 0 )
|
|
{
|
|
data[ offset ] = counter;
|
|
}
|
|
}
|
|
|
|
static int counter_from_ram( uint8_t *data, int offset )
|
|
{
|
|
if( offset >= 0 )
|
|
{
|
|
return data[ offset ];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//**************************************************************************
|
|
// LIVE DEVICE
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// timekeeper_device_config - constructor
|
|
//-------------------------------------------------
|
|
|
|
timekeeper_device::timekeeper_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, int size)
|
|
: device_t(mconfig, type, tag, owner, clock)
|
|
, device_nvram_interface(mconfig, *this)
|
|
, m_reset_cb(*this)
|
|
, m_irq_cb(*this)
|
|
, m_default_data(*this, DEVICE_SELF, size)
|
|
, m_size(size)
|
|
{
|
|
}
|
|
|
|
m48t02_device::m48t02_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: timekeeper_device(mconfig, M48T02, tag, owner, clock, 0x800)
|
|
{
|
|
m_offset_watchdog = -1;
|
|
m_offset_control = 0x7f8;
|
|
m_offset_seconds = 0x7f9;
|
|
m_offset_minutes = 0x7fa;
|
|
m_offset_hours = 0x7fb;
|
|
m_offset_day = 0x7fc;
|
|
m_offset_date = 0x7fd;
|
|
m_offset_month = 0x7fe;
|
|
m_offset_year = 0x7ff;
|
|
m_offset_century = -1;
|
|
m_offset_flags = -1;
|
|
}
|
|
|
|
m48t35_device::m48t35_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: timekeeper_device(mconfig, M48T35, tag, owner, clock, 0x8000)
|
|
{
|
|
m_offset_watchdog = -1;
|
|
m_offset_control = 0x7ff8;
|
|
m_offset_seconds = 0x7ff9;
|
|
m_offset_minutes = 0x7ffa;
|
|
m_offset_hours = 0x7ffb;
|
|
m_offset_day = 0x7ffc;
|
|
m_offset_date = 0x7ffd;
|
|
m_offset_month = 0x7ffe;
|
|
m_offset_year = 0x7fff;
|
|
m_offset_century = -1;
|
|
m_offset_flags = -1;
|
|
}
|
|
|
|
m48t37_device::m48t37_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: timekeeper_device(mconfig, M48T37, tag, owner, clock, 0x8000)
|
|
{
|
|
m_offset_watchdog = 0x7ff7;
|
|
m_offset_control = 0x7ff8;
|
|
m_offset_seconds = 0x7ff9;
|
|
m_offset_minutes = 0x7ffa;
|
|
m_offset_hours = 0x7ffb;
|
|
m_offset_day = 0x7ffc;
|
|
m_offset_date = 0x7ffd;
|
|
m_offset_month = 0x7ffe;
|
|
m_offset_year = 0x7fff;
|
|
m_offset_century = 0x7ff1;
|
|
m_offset_flags = 0x7ff0;
|
|
}
|
|
|
|
m48t58_device::m48t58_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: timekeeper_device(mconfig, M48T58, tag, owner, clock, 0x2000)
|
|
{
|
|
m_offset_watchdog = -1;
|
|
m_offset_control = 0x1ff8;
|
|
m_offset_seconds = 0x1ff9;
|
|
m_offset_minutes = 0x1ffa;
|
|
m_offset_hours = 0x1ffb;
|
|
m_offset_day = 0x1ffc;
|
|
m_offset_date = 0x1ffd;
|
|
m_offset_month = 0x1ffe;
|
|
m_offset_year = 0x1fff;
|
|
m_offset_century = -1;
|
|
m_offset_flags = -1;
|
|
}
|
|
|
|
mk48t08_device::mk48t08_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: timekeeper_device(mconfig, MK48T08, tag, owner, clock, 0x2000)
|
|
{
|
|
m_offset_watchdog = -1;
|
|
m_offset_control = 0x1ff8;
|
|
m_offset_seconds = 0x1ff9;
|
|
m_offset_minutes = 0x1ffa;
|
|
m_offset_hours = 0x1ffb;
|
|
m_offset_day = 0x1ffc;
|
|
m_offset_date = 0x1ffd;
|
|
m_offset_month = 0x1ffe;
|
|
m_offset_year = 0x1fff;
|
|
m_offset_century = 0x1ff1;
|
|
m_offset_flags = 0x1ff0;
|
|
}
|
|
|
|
mk48t12_device::mk48t12_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: timekeeper_device(mconfig, MK48T12, tag, owner, clock, 0x800)
|
|
{
|
|
m_offset_watchdog = -1;
|
|
m_offset_control = 0x7f8;
|
|
m_offset_seconds = 0x7f9;
|
|
m_offset_minutes = 0x7fa;
|
|
m_offset_hours = 0x7fb;
|
|
m_offset_day = 0x7fc;
|
|
m_offset_date = 0x7fd;
|
|
m_offset_month = 0x7fe;
|
|
m_offset_year = 0x7ff;
|
|
m_offset_century = -1;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// device_start - device-specific startup
|
|
//-------------------------------------------------
|
|
|
|
void timekeeper_device::device_start()
|
|
{
|
|
system_time systime;
|
|
|
|
/* validate some basic stuff */
|
|
assert(this != nullptr);
|
|
|
|
machine().base_datetime(systime);
|
|
|
|
m_control = 0;
|
|
m_seconds = time_helper::make_bcd( systime.local_time.second );
|
|
m_minutes = time_helper::make_bcd( systime.local_time.minute );
|
|
m_hours = time_helper::make_bcd( systime.local_time.hour );
|
|
m_day = time_helper::make_bcd( systime.local_time.weekday + 1 );
|
|
m_date = time_helper::make_bcd( systime.local_time.mday );
|
|
m_month = time_helper::make_bcd( systime.local_time.month + 1 );
|
|
m_year = time_helper::make_bcd( systime.local_time.year % 100 );
|
|
m_century = time_helper::make_bcd( systime.local_time.year / 100 );
|
|
m_data.resize( m_size );
|
|
|
|
save_item( NAME(m_control) );
|
|
save_item( NAME(m_seconds) );
|
|
save_item( NAME(m_minutes) );
|
|
save_item( NAME(m_hours) );
|
|
save_item( NAME(m_day) );
|
|
save_item( NAME(m_date) );
|
|
save_item( NAME(m_month) );
|
|
save_item( NAME(m_year) );
|
|
save_item( NAME(m_century) );
|
|
save_item( NAME(m_data) );
|
|
save_item( NAME(m_watchdog_delay));
|
|
|
|
emu_timer *timer = timer_alloc();
|
|
timer->adjust(attotime::from_seconds(1), 0, attotime::from_seconds(1));
|
|
|
|
m_watchdog_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(timekeeper_device::watchdog_callback), this));
|
|
m_watchdog_timer->adjust(attotime::never);
|
|
m_reset_cb.resolve_safe();
|
|
m_irq_cb.resolve_safe();
|
|
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// device_reset - device-specific reset
|
|
//-------------------------------------------------
|
|
|
|
void timekeeper_device::device_reset() { }
|
|
|
|
void timekeeper_device::counters_to_ram()
|
|
{
|
|
counter_to_ram( &m_data[0], m_offset_control, m_control );
|
|
counter_to_ram( &m_data[0], m_offset_seconds, m_seconds );
|
|
counter_to_ram( &m_data[0], m_offset_minutes, m_minutes );
|
|
counter_to_ram( &m_data[0], m_offset_hours, m_hours );
|
|
counter_to_ram( &m_data[0], m_offset_day, m_day );
|
|
counter_to_ram( &m_data[0], m_offset_date, m_date );
|
|
counter_to_ram( &m_data[0], m_offset_month, m_month );
|
|
counter_to_ram( &m_data[0], m_offset_year, m_year );
|
|
counter_to_ram( &m_data[0], m_offset_century, m_century );
|
|
}
|
|
|
|
void timekeeper_device::counters_from_ram()
|
|
{
|
|
m_control = counter_from_ram( &m_data[0], m_offset_control );
|
|
m_seconds = counter_from_ram( &m_data[0], m_offset_seconds );
|
|
m_minutes = counter_from_ram( &m_data[0], m_offset_minutes );
|
|
m_hours = counter_from_ram( &m_data[0], m_offset_hours );
|
|
m_day = counter_from_ram( &m_data[0], m_offset_day );
|
|
m_date = counter_from_ram( &m_data[0], m_offset_date );
|
|
m_month = counter_from_ram( &m_data[0], m_offset_month );
|
|
m_year = counter_from_ram( &m_data[0], m_offset_year );
|
|
m_century = counter_from_ram( &m_data[0], m_offset_century );
|
|
}
|
|
|
|
void timekeeper_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
|
{
|
|
LOGMASKED(LOG_TICKS, "Tick\n");
|
|
if( ( m_seconds & SECONDS_ST ) != 0 ||
|
|
( m_control & CONTROL_W ) != 0 )
|
|
{
|
|
logerror("No Tick\n");
|
|
return;
|
|
}
|
|
|
|
int carry = time_helper::inc_bcd( &m_seconds, MASK_SECONDS, 0x00, 0x59 );
|
|
if( carry )
|
|
{
|
|
carry = time_helper::inc_bcd( &m_minutes, MASK_MINUTES, 0x00, 0x59 );
|
|
}
|
|
if( carry )
|
|
{
|
|
carry = time_helper::inc_bcd( &m_hours, MASK_HOURS, 0x00, 0x23 );
|
|
}
|
|
|
|
if( carry )
|
|
{
|
|
uint8_t maxdays;
|
|
static const uint8_t daysinmonth[] = { 0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31 };
|
|
|
|
time_helper::inc_bcd( &m_day, MASK_DAY, 0x01, 0x07 );
|
|
|
|
uint8_t month = time_helper::from_bcd( m_month );
|
|
uint8_t year = time_helper::from_bcd( m_year );
|
|
|
|
if( month == 2 && ( year % 4 ) == 0 )
|
|
{
|
|
maxdays = 0x29;
|
|
}
|
|
else if( month >= 1 && month <= 12 )
|
|
{
|
|
maxdays = daysinmonth[ month - 1 ];
|
|
}
|
|
else
|
|
{
|
|
maxdays = 0x31;
|
|
}
|
|
|
|
carry = time_helper::inc_bcd( &m_date, MASK_DATE, 0x01, maxdays );
|
|
}
|
|
if( carry )
|
|
{
|
|
carry = time_helper::inc_bcd( &m_month, MASK_MONTH, 0x01, 0x12 );
|
|
}
|
|
if( carry )
|
|
{
|
|
carry = time_helper::inc_bcd( &m_year, MASK_YEAR, 0x00, 0x99 );
|
|
}
|
|
if( carry )
|
|
{
|
|
carry = time_helper::inc_bcd( &m_century, MASK_CENTURY, 0x00, 0x99 );
|
|
|
|
if( type() == M48T35 ||
|
|
type() == M48T58 )
|
|
{
|
|
if( ( m_day & DAY_CEB ) != 0 )
|
|
{
|
|
m_day ^= DAY_CB;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( ( m_control & CONTROL_R ) == 0 )
|
|
{
|
|
counters_to_ram();
|
|
}
|
|
}
|
|
|
|
TIMER_CALLBACK_MEMBER(timekeeper_device::watchdog_callback)
|
|
{
|
|
// Set Flag
|
|
m_data[m_offset_flags] |= FLAGS_WDF;
|
|
// WDS (bit 7) selects callback
|
|
if (m_data[m_offset_watchdog] & 0x80) {
|
|
// Clear watchdog register
|
|
m_data[m_offset_watchdog] = 0;
|
|
m_reset_cb(ASSERT_LINE);
|
|
}
|
|
else {
|
|
m_irq_cb(ASSERT_LINE);
|
|
}
|
|
logerror("watchdog_callback: WD Control: %02x WD Flags: %02x\n", m_data[m_offset_watchdog], m_data[m_offset_flags]);
|
|
}
|
|
|
|
WRITE8_MEMBER(timekeeper_device::watchdog_write)
|
|
{
|
|
if ((m_data[m_offset_watchdog] & 0x7f) != 0) {
|
|
m_watchdog_timer->adjust(m_watchdog_delay);
|
|
}
|
|
}
|
|
|
|
WRITE8_MEMBER( timekeeper_device::write )
|
|
{
|
|
LOGMASKED(LOG_GENERAL, "timekeeper_device::write: %04x = %02x\n", offset, data);
|
|
if( offset == m_offset_control )
|
|
{
|
|
if( ( m_control & CONTROL_W ) != 0 &&
|
|
( data & CONTROL_W ) == 0 )
|
|
{
|
|
counters_from_ram();
|
|
}
|
|
|
|
if( ( m_control & CONTROL_R ) != 0 &&
|
|
( data & CONTROL_W ) == 0 )
|
|
{
|
|
counters_to_ram();
|
|
}
|
|
|
|
m_control = data;
|
|
}
|
|
else if( offset == m_offset_day )
|
|
{
|
|
if( type() == M48T35 ||
|
|
type() == M48T58 )
|
|
{
|
|
m_day = ( m_day & ~DAY_CEB ) | ( data & DAY_CEB );
|
|
}
|
|
}
|
|
else if (offset == m_offset_watchdog && type() == M48T37)
|
|
{
|
|
if ((data & 0x7f) == 0) {
|
|
m_watchdog_timer->adjust(attotime::never);
|
|
}
|
|
else {
|
|
// Calculate the time unit
|
|
m_watchdog_delay = attotime::from_usec(62500 << (2 * (data & 0x3)));
|
|
// Adjust by multiplier
|
|
m_watchdog_delay *= (data >> 2) & 0x1f;
|
|
m_watchdog_timer->adjust(m_watchdog_delay);
|
|
//logerror("write: setting watchdog to %s WatchdogReg = 0x%02x\n", m_watchdog_delay.as_string(), data);
|
|
}
|
|
}
|
|
|
|
m_data[ offset ] = data;
|
|
}
|
|
|
|
READ8_MEMBER( timekeeper_device::read )
|
|
{
|
|
uint8_t result = m_data[ offset ];
|
|
if( offset == m_offset_date && type() == M48T58 )
|
|
{
|
|
result &= ~DATE_BL;
|
|
}
|
|
else if( offset == m_offset_flags && type() == M48T37 )
|
|
{
|
|
// Clear the watchdog flag
|
|
m_data[m_offset_flags] &= ~FLAGS_WDF;
|
|
// Clear callbacks
|
|
m_reset_cb(CLEAR_LINE);
|
|
m_irq_cb(CLEAR_LINE);
|
|
}
|
|
LOGMASKED(LOG_GENERAL, "timekeeper_device::read: %04x (%02x)\n", offset, result);
|
|
return result;
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// nvram_default - called to initialize NVRAM to
|
|
// its default state
|
|
//-------------------------------------------------
|
|
|
|
void timekeeper_device::nvram_default()
|
|
{
|
|
if (m_default_data.found())
|
|
{
|
|
memcpy( &m_data[0], m_default_data, m_size );
|
|
}
|
|
else
|
|
{
|
|
memset( &m_data[0], 0xff, m_data.size());
|
|
}
|
|
|
|
if ( m_offset_flags >= 0 )
|
|
m_data[ m_offset_flags ] = 0;
|
|
counters_to_ram();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// nvram_read - called to read NVRAM from the
|
|
// .nv file
|
|
//-------------------------------------------------
|
|
|
|
void timekeeper_device::nvram_read(emu_file &file)
|
|
{
|
|
file.read( &m_data[0], m_size );
|
|
|
|
counters_to_ram();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// nvram_write - called to write NVRAM to the
|
|
// .nv file
|
|
//-------------------------------------------------
|
|
|
|
void timekeeper_device::nvram_write(emu_file &file)
|
|
{
|
|
file.write( &m_data[0], m_size );
|
|
}
|