summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/msm58321.cpp
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2015-11-08 12:56:12 +0100
committer Miodrag Milanovic <mmicko@gmail.com>2015-11-08 12:56:12 +0100
commit7c19aac60e12d6f5ea301bdb34d7826a01e0b06f (patch)
treef310d86aa2c6bfc19d115307dedde4eb0cd52dad /src/devices/machine/msm58321.cpp
parenta57b46ae933badd7441ce1644711dbb851e2b504 (diff)
Rename *.c -> *.cpp in our source (nw)
Diffstat (limited to 'src/devices/machine/msm58321.cpp')
-rw-r--r--src/devices/machine/msm58321.cpp646
1 files changed, 646 insertions, 0 deletions
diff --git a/src/devices/machine/msm58321.cpp b/src/devices/machine/msm58321.cpp
new file mode 100644
index 00000000000..679b85584ff
--- /dev/null
+++ b/src/devices/machine/msm58321.cpp
@@ -0,0 +1,646 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ OKI MSM58321RS Real Time Clock/Calendar emulation
+
+**********************************************************************/
+
+/*
+
+ TODO:
+
+ - leap year
+ - test
+ - reference registers
+
+*/
+
+#include "msm58321.h"
+
+
+// device type definition
+const device_type MSM58321 = &device_creator<msm58321_device>;
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+// registers
+enum
+{
+ REGISTER_S1 = 0,
+ REGISTER_S10,
+ REGISTER_MI1,
+ REGISTER_MI10,
+ REGISTER_H1,
+ REGISTER_H10,
+ REGISTER_W,
+ REGISTER_D1,
+ REGISTER_D10,
+ REGISTER_MO1,
+ REGISTER_MO10,
+ REGISTER_Y1,
+ REGISTER_Y10,
+ REGISTER_RESET,
+ REGISTER_REF0,
+ REGISTER_REF1
+};
+
+static const char *reg_name(UINT8 address)
+{
+ switch(address)
+ {
+ case REGISTER_S1: return "S1";
+ case REGISTER_S10: return "S10";
+ case REGISTER_MI1: return "MI1";
+ case REGISTER_MI10: return "MI10";
+ case REGISTER_H1: return "H1";
+ case REGISTER_H10: return "H10";
+ case REGISTER_W: return "W";
+ case REGISTER_D1: return "D1";
+ case REGISTER_D10: return "D10";
+ case REGISTER_MO1: return "MO1";
+ case REGISTER_MO10: return "MO10";
+ case REGISTER_Y1: return "Y1";
+ case REGISTER_Y10: return "Y10";
+ case REGISTER_RESET: return "RESET";
+ case REGISTER_REF0: return "REF0";
+ case REGISTER_REF1: return "REF1";
+ }
+
+ return "INVALID REGISTER";
+}
+
+enum
+{
+ H10_PM = 4,
+ H10_24 = 8
+};
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// read_counter -
+//-------------------------------------------------
+
+inline int msm58321_device::read_counter(int counter)
+{
+ int data = m_reg[counter];
+
+ if (counter == REGISTER_H1)
+ {
+ int h10 = m_reg[REGISTER_H10];
+
+ if (h10 & H10_24)
+ {
+ data += (h10 & 3) * 10;
+ }
+ else
+ {
+ data += (h10 & 1) * 10;
+
+ if (h10 & H10_PM)
+ {
+ if (data != 12)
+ {
+ data += 12;
+ }
+ }
+ else if (data == 12)
+ {
+ data = 0;
+ }
+ }
+ }
+ else
+ {
+ data += (m_reg[counter + 1] * 10);
+ }
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// write_counter -
+//-------------------------------------------------
+
+inline void msm58321_device::write_counter(int address, int data)
+{
+ int flag = 0;
+
+ switch (address)
+ {
+ case REGISTER_H1:
+ flag = m_reg[REGISTER_H10] & H10_24;
+ if (!flag)
+ {
+ if (data >= 12)
+ {
+ data -= 12;
+ flag = H10_PM;
+ }
+
+ if (data == 0)
+ {
+ data = 12;
+ }
+ }
+ break;
+
+ case REGISTER_D1:
+ flag = (m_reg[REGISTER_D10] & ~3);
+ break;
+ }
+
+ m_reg[address] = data % 10;
+ m_reg[address + 1] = (data / 10) | flag;
+}
+
+
+
+//-------------------------------------------------
+// msm58321_device - constructor
+//-------------------------------------------------
+
+msm58321_device::msm58321_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MSM58321, "MSM58321", tag, owner, clock, "msm58321", __FILE__),
+ device_rtc_interface(mconfig, *this),
+ device_nvram_interface(mconfig, *this),
+ m_year0(0),
+ m_default_24h(false),
+ m_d0_handler(*this),
+ m_d1_handler(*this),
+ m_d2_handler(*this),
+ m_d3_handler(*this),
+ m_busy_handler(*this),
+ m_cs2(0),
+ m_write(0),
+ m_read(0),
+ m_d0_in(0),
+ m_d0_out(0),
+ m_d1_in(0),
+ m_d1_out(0),
+ m_d2_in(0),
+ m_d2_out(0),
+ m_d3_in(0),
+ m_d3_out(0),
+ m_address_write(0),
+ m_busy(0),
+ m_stop(0),
+ m_test(0),
+ m_cs1(0),
+ m_address(0xf)
+{
+ memset(m_reg, 0x00, sizeof(m_reg));
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void msm58321_device::device_start()
+{
+ // resolve callbacks
+ m_d0_handler.resolve_safe();
+ m_d1_handler.resolve_safe();
+ m_d2_handler.resolve_safe();
+ m_d3_handler.resolve_safe();
+ m_busy_handler.resolve_safe();
+
+ // allocate timers
+ m_clock_timer = timer_alloc(TIMER_CLOCK);
+ m_clock_timer->adjust(attotime::from_hz(clock() / 32768), 0, attotime::from_hz(clock() / 32768));
+
+ m_busy_timer = timer_alloc(TIMER_BUSY);
+ m_busy_timer->adjust(attotime::from_hz(clock() / 16384), 0, attotime::from_hz(clock() / 16384));
+
+ // state saving
+ save_item(NAME(m_cs2));
+ save_item(NAME(m_write));
+ save_item(NAME(m_read));
+ save_item(NAME(m_d0_in));
+ save_item(NAME(m_d0_out));
+ save_item(NAME(m_d1_in));
+ save_item(NAME(m_d1_out));
+ save_item(NAME(m_d2_in));
+ save_item(NAME(m_d2_out));
+ save_item(NAME(m_d3_in));
+ save_item(NAME(m_d3_out));
+ save_item(NAME(m_address_write));
+ save_item(NAME(m_busy));
+ save_item(NAME(m_stop));
+ save_item(NAME(m_test));
+ save_item(NAME(m_cs1));
+ save_item(NAME(m_address));
+ save_item(NAME(m_reg));
+
+ set_current_time(machine());
+
+ update_output();
+}
+
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+
+void msm58321_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_CLOCK:
+ if (!m_stop)
+ advance_seconds();
+ break;
+
+ case TIMER_BUSY:
+ if (!m_cs1 || !m_cs2 || !m_write || m_address != REGISTER_RESET)
+ {
+ m_busy = !m_busy;
+ m_busy_handler(m_busy);
+ }
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// rtc_clock_updated -
+//-------------------------------------------------
+
+void msm58321_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second)
+{
+ write_counter(REGISTER_Y1, (year - m_year0) % 100);
+ write_counter(REGISTER_MO1, month);
+ write_counter(REGISTER_D1, day);
+ m_reg[REGISTER_W] = day_of_week;
+ write_counter(REGISTER_H1, hour);
+ write_counter(REGISTER_MI1, minute);
+ write_counter(REGISTER_S1, second);
+
+ update_output();
+}
+
+//-------------------------------------------------
+// nvram_default - called to initialize NVRAM to
+// its default state
+//-------------------------------------------------
+
+void msm58321_device::nvram_default()
+{
+ for (int i = 0; i < 13; i++)
+ m_reg[i] = 0;
+
+ if (m_default_24h)
+ m_reg[REGISTER_H10] = H10_24;
+
+ clock_updated();
+}
+
+
+//-------------------------------------------------
+// nvram_read - called to read NVRAM from the
+// .nv file
+//-------------------------------------------------
+
+void msm58321_device::nvram_read(emu_file &file)
+{
+ file.read(m_reg, sizeof(m_reg));
+
+ clock_updated();
+}
+
+
+//-------------------------------------------------
+// nvram_write - called to write NVRAM to the
+// .nv file
+//-------------------------------------------------
+
+void msm58321_device::nvram_write(emu_file &file)
+{
+ file.write(m_reg, sizeof(m_reg));
+}
+
+//-------------------------------------------------
+// update_output -
+//-------------------------------------------------
+
+void msm58321_device::update_output()
+{
+ UINT8 data = 0xf;
+
+ if (m_cs1 && m_cs2 && m_read)
+ {
+ switch (m_address)
+ {
+ case REGISTER_RESET:
+ data = 0;
+ break;
+
+ case REGISTER_REF0:
+ case REGISTER_REF1:
+ // TODO: output reference values
+ data = 0;
+ break;
+
+ default:
+ data = m_reg[m_address];
+ break;
+ }
+
+ if (LOG) logerror("MSM58321 '%s' Register Read %s (%01x): %01x\n", tag(), reg_name(m_address), m_address, data & 0x0f);
+ }
+
+ int d0 = (data >> 0) & 1;
+ if (m_d0_out != d0)
+ {
+ m_d0_out = d0;
+ m_d0_handler(d0);
+ }
+
+ int d1 = (data >> 1) & 1;
+ if (m_d1_out != d1)
+ {
+ m_d1_out = d1;
+ m_d1_handler(d1);
+ }
+
+ int d2 = (data >> 2) & 1;
+ if (m_d2_out != d2)
+ {
+ m_d2_out = d2;
+ m_d2_handler(d2);
+ }
+
+ int d3 = (data >> 3) & 1;
+ if (m_d3_out != d3)
+ {
+ m_d3_out = d3;
+ m_d3_handler(d3);
+ }
+}
+
+
+//-------------------------------------------------
+// update_input() -
+//-------------------------------------------------
+
+void msm58321_device::update_input()
+{
+ if (m_cs1 && m_cs2)
+ {
+ UINT8 data = m_d0_in | (m_d1_in << 1) | (m_d2_in << 2) | (m_d3_in << 3);
+
+ if (m_address_write)
+ {
+ if (LOG) logerror("MSM58321 '%s' Latch Address %01x\n", tag(), data);
+
+ // latch address
+ m_address = data;
+ }
+
+ if (m_write)
+ {
+ switch(m_address)
+ {
+ case REGISTER_RESET:
+ if (LOG) logerror("MSM58321 '%s' Reset\n", tag());
+
+ if (!m_busy)
+ {
+ m_busy = 1;
+ m_busy_handler(m_busy);
+ }
+ break;
+
+ case REGISTER_REF0:
+ case REGISTER_REF1:
+ if (LOG) logerror("MSM58321 '%s' Reference Signal\n", tag());
+ break;
+
+ default:
+ if (LOG) logerror("MSM58321 '%s' Register Write %s (%01x): %01x\n", tag(), reg_name(m_address), m_address, data);
+
+ switch (m_address)
+ {
+ case REGISTER_S10:
+ case REGISTER_MI10:
+ case REGISTER_W:
+ m_reg[m_address] = data & 7;
+ break;
+
+ case REGISTER_H10:
+ if (data & H10_24)
+ {
+ // "When D3 = 1 is written, the D2 bit is reset inside the IC."
+ // but it doesn't say if this is done immediately or on the next update
+ m_reg[m_address] = data & ~H10_PM;
+ }
+ else
+ {
+ m_reg[m_address] = data;
+ }
+ break;
+
+ case REGISTER_MO10:
+ m_reg[m_address] = data & 1;
+ break;
+
+ default:
+ m_reg[m_address] = data;
+ break;
+ }
+
+ set_time(false, read_counter(REGISTER_Y1) + m_year0, read_counter(REGISTER_MO1), read_counter(REGISTER_D1), m_reg[REGISTER_W],
+ read_counter(REGISTER_H1), read_counter(REGISTER_MI1), read_counter(REGISTER_S1));
+ break;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------
+// cs2_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( msm58321_device::cs2_w )
+{
+ if (m_cs2 != state)
+ {
+ if (LOG) logerror("MSM58321 '%s' CS2: %u\n", tag(), state);
+
+ m_cs2 = state;
+
+ update_input();
+ }
+}
+
+
+//-------------------------------------------------
+// write_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( msm58321_device::write_w )
+{
+ if (m_write != state)
+ {
+ if (LOG) logerror("MSM58321 '%s' WRITE: %u\n", tag(), state);
+
+ m_write = state;
+
+ update_input();
+ }
+}
+
+
+//-------------------------------------------------
+// read_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( msm58321_device::read_w )
+{
+ if (m_read != state)
+ {
+ if (LOG) logerror("MSM58321 '%s' READ: %u\n", tag(), state);
+
+ m_read = state;
+
+ update_output();
+ }
+}
+
+
+
+//-------------------------------------------------
+// d0_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( msm58321_device::d0_w )
+{
+ if (m_d0_in != state)
+ {
+ m_d0_in = state;
+
+ update_input();
+ }
+}
+
+
+//-------------------------------------------------
+// d1_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( msm58321_device::d1_w )
+{
+ if (m_d1_in != state)
+ {
+ m_d1_in = state;
+
+ update_input();
+ }
+}
+
+
+//-------------------------------------------------
+// d2_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( msm58321_device::d2_w )
+{
+ if (m_d2_in != state)
+ {
+ m_d2_in = state;
+
+ update_input();
+ }
+}
+
+
+//-------------------------------------------------
+// d3_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( msm58321_device::d3_w )
+{
+ if (m_d3_in != state)
+ {
+ m_d3_in = state;
+
+ update_input();
+ }
+}
+
+
+//-------------------------------------------------
+// address_write_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( msm58321_device::address_write_w )
+{
+ if (m_address_write != state)
+ {
+ if (LOG) logerror("MSM58321 '%s' ADDRESS WRITE: %u\n", tag(), state);
+
+ m_address_write = state;
+
+ update_input();
+ }
+}
+
+
+//-------------------------------------------------
+// stop_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( msm58321_device::stop_w )
+{
+ if (m_stop != state)
+ {
+ if (LOG) logerror("MSM58321 '%s' STOP: %u\n", tag(), state);
+
+ m_stop = state;
+ }
+}
+
+
+//-------------------------------------------------
+// test_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( msm58321_device::test_w )
+{
+ if (m_test != state)
+ {
+ if (LOG) logerror("MSM58321 '%s' TEST: %u\n", tag(), state);
+
+ m_test = state;
+ }
+}
+
+
+
+//-------------------------------------------------
+// cs1_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( msm58321_device::cs1_w )
+{
+ if (m_cs1 != state)
+ {
+ if (LOG) logerror("MSM58321 '%s' CS1: %u\n", tag(), state);
+
+ m_cs1 = state;
+
+ update_input();
+ }
+}