diff options
author | 2015-11-08 12:56:12 +0100 | |
---|---|---|
committer | 2015-11-08 12:56:12 +0100 | |
commit | 7c19aac60e12d6f5ea301bdb34d7826a01e0b06f (patch) | |
tree | f310d86aa2c6bfc19d115307dedde4eb0cd52dad /src/devices/machine/msm58321.cpp | |
parent | a57b46ae933badd7441ce1644711dbb851e2b504 (diff) |
Rename *.c -> *.cpp in our source (nw)
Diffstat (limited to 'src/devices/machine/msm58321.cpp')
-rw-r--r-- | src/devices/machine/msm58321.cpp | 646 |
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(); + } +} |