diff options
Diffstat (limited to 'trunk/src/emu/machine/upd1990a.c')
-rw-r--r-- | trunk/src/emu/machine/upd1990a.c | 526 |
1 files changed, 526 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/upd1990a.c b/trunk/src/emu/machine/upd1990a.c new file mode 100644 index 00000000000..51b60011107 --- /dev/null +++ b/trunk/src/emu/machine/upd1990a.c @@ -0,0 +1,526 @@ +/********************************************************************** + + NEC uPD1990AC Serial I/O Calendar & Clock emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +/* + + TODO: + + - test mode + +*/ + +#include "upd1990a.h" + + +// device type definition +const device_type UPD1990A = &device_creator<upd1990a_device>; + + +//************************************************************************** +// MACROS / CONSTANTS +//************************************************************************** + +#define LOG 0 + + +// operating modes +enum +{ + MODE_REGISTER_HOLD = 0, + MODE_SHIFT, + MODE_TIME_SET, + MODE_TIME_READ, + MODE_TP_64HZ_SET, + MODE_TP_256HZ_SET, + MODE_TP_2048HZ_SET, + MODE_TEST +}; + + + +//************************************************************************** +// INLINE HELPERS +//************************************************************************** + +//------------------------------------------------- +// convert_to_bcd - +//------------------------------------------------- + +inline UINT8 upd1990a_device::convert_to_bcd(int val) +{ + return ((val / 10) << 4) | (val % 10); +} + + +//------------------------------------------------- +// bcd_to_integer - +//------------------------------------------------- + +inline int upd1990a_device::bcd_to_integer(UINT8 val) +{ + return (((val & 0xf0) >> 4) * 10) + (val & 0x0f); +} + + +//------------------------------------------------- +// advance_seconds - +//------------------------------------------------- + +inline void upd1990a_device::advance_seconds() +{ + static const int days_per_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + int seconds = bcd_to_integer(m_time_counter[0]); + int minutes = bcd_to_integer(m_time_counter[1]); + int hours = bcd_to_integer(m_time_counter[2]); + int days = bcd_to_integer(m_time_counter[3]); + int day_of_week = m_time_counter[4] & 0x0f; + int month = (m_time_counter[4] & 0xf0) >> 4; + + seconds++; + + if (seconds > 59) + { + seconds = 0; + minutes++; + } + + if (minutes > 59) + { + minutes = 0; + hours++; + } + + if (hours > 23) + { + hours = 0; + days++; + day_of_week++; + } + + if (day_of_week > 6) + { + day_of_week++; + } + + if (days > days_per_month[month - 1]) + { + days = 1; + month++; + } + + if (month > 12) + { + month = 1; + } + + m_time_counter[0] = convert_to_bcd(seconds); + m_time_counter[1] = convert_to_bcd(minutes); + m_time_counter[2] = convert_to_bcd(hours); + m_time_counter[3] = convert_to_bcd(days); + m_time_counter[4] = (month << 4) | day_of_week; +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// upd1990a_device - constructor +//------------------------------------------------- + +upd1990a_device::upd1990a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, UPD1990A, "uPD1990A", tag, owner, clock), + device_rtc_interface(mconfig, *this) +{ +} + + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void upd1990a_device::device_config_complete() +{ + // inherit a copy of the static data + const upd1990a_interface *intf = reinterpret_cast<const upd1990a_interface *>(static_config()); + if (intf != NULL) + *static_cast<upd1990a_interface *>(this) = *intf; + + // or initialize to defaults if none provided + else + { + memset(&m_out_data_cb, 0, sizeof(m_out_data_cb)); + memset(&m_out_tp_cb, 0, sizeof(m_out_tp_cb)); + } +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void upd1990a_device::device_start() +{ + // resolve callbacks + m_out_data_func.resolve(m_out_data_cb, *this); + m_out_tp_func.resolve(m_out_tp_cb, *this); + + // allocate timers + m_timer_clock = timer_alloc(TIMER_CLOCK); + m_timer_clock->adjust(attotime::from_hz(clock() / 32768), 0, attotime::from_hz(clock() / 32768)); + m_timer_tp = timer_alloc(TIMER_TP); + m_timer_data_out = timer_alloc(TIMER_DATA_OUT); + + // state saving + save_item(NAME(m_time_counter)); + save_item(NAME(m_shift_reg)); + save_item(NAME(m_oe)); + save_item(NAME(m_cs)); + save_item(NAME(m_stb)); + save_item(NAME(m_data_in)); + save_item(NAME(m_data_out)); + save_item(NAME(m_c)); + save_item(NAME(m_clk)); + save_item(NAME(m_tp)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void upd1990a_device::device_reset() +{ + system_time curtime, *systime = &curtime; + + machine().current_datetime(curtime); + + // HACK: load time counter from system time + m_time_counter[0] = convert_to_bcd(systime->local_time.second); + m_time_counter[1] = convert_to_bcd(systime->local_time.minute); + m_time_counter[2] = convert_to_bcd(systime->local_time.hour); + m_time_counter[3] = convert_to_bcd(systime->local_time.mday); + m_time_counter[4] = systime->local_time.weekday; + m_time_counter[4] |= (systime->local_time.month + 1) << 4; +} + + +//------------------------------------------------- +// device_timer - handler timer events +//------------------------------------------------- + +void upd1990a_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + case TIMER_CLOCK: + advance_seconds(); + break; + + case TIMER_TP: + m_tp = !m_tp; + + if (LOG) logerror("uPD1990A '%s' TP %u\n", tag(), m_tp); + + m_out_tp_func(m_tp); + break; + + case TIMER_DATA_OUT: + m_data_out = !m_data_out; + + if (LOG) logerror("uPD1990A '%s' DATA OUT TICK %u\n", tag(), m_data_out); + + m_out_data_func(m_data_out); + break; + } +} + + +//------------------------------------------------- +// rtc_set_time - called to initialize the RTC to +// a known state +//------------------------------------------------- + +void upd1990a_device::rtc_set_time(int year, int month, int day, int day_of_week, int hour, int minute, int second) +{ + m_time_counter[0] = convert_to_bcd(second); + m_time_counter[1] = convert_to_bcd(minute); + m_time_counter[2] = convert_to_bcd(hour); + m_time_counter[3] = convert_to_bcd(day); + m_time_counter[4] = (month << 4) | day_of_week; +} + + +//------------------------------------------------- +// oe_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::oe_w ) +{ + if (LOG) logerror("uPD1990A '%s' OE %u\n", tag(), state); + + m_oe = state; +} + + +//------------------------------------------------- +// cs_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::cs_w ) +{ + if (LOG) logerror("uPD1990A '%s' CS %u\n", tag(), state); + + m_cs = state; +} + + +//------------------------------------------------- +// stb_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::stb_w ) +{ + if (LOG) logerror("uPD1990A '%s' STB %u\n", tag(), state); + + m_stb = state; + + if (m_cs && m_stb && !m_clk) + { + switch (m_c) + { + case MODE_REGISTER_HOLD: + if (LOG) logerror("uPD1990A '%s' Register Hold Mode\n", tag()); + + /* enable time counter */ + m_timer_clock->enable(1); + + /* 1 Hz data out pulse */ + m_data_out = 1; + m_timer_data_out->adjust(attotime::zero, 0, attotime::from_hz(1*2)); + + /* 64 Hz time pulse */ + m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(64*2)); + break; + + case MODE_SHIFT: + if (LOG) logerror("uPD1990A '%s' Shift Mode\n", tag()); + + /* enable time counter */ + m_timer_clock->enable(1); + + /* disable data out pulse */ + m_timer_data_out->enable(0); + + /* output LSB of shift register */ + m_data_out = BIT(m_shift_reg[0], 0); + m_out_data_func(m_data_out); + + /* 32 Hz time pulse */ + m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(32*2)); + break; + + case MODE_TIME_SET: + if (LOG) logerror("uPD1990A '%s' Time Set Mode\n", tag()); + if (LOG) logerror("uPD1990A '%s' Shift Register %02x%02x%02x%02x%02x\n", tag(), m_shift_reg[4], m_shift_reg[3], m_shift_reg[2], m_shift_reg[1], m_shift_reg[0]); + + /* disable time counter */ + m_timer_clock->enable(0); + + /* disable data out pulse */ + m_timer_data_out->enable(0); + + /* output LSB of shift register */ + m_data_out = BIT(m_shift_reg[0], 0); + m_out_data_func(m_data_out); + + /* load shift register data into time counter */ + for (int i = 0; i < 5; i++) + { + m_time_counter[i] = m_shift_reg[i]; + } + + /* 32 Hz time pulse */ + m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(32*2)); + break; + + case MODE_TIME_READ: + if (LOG) logerror("uPD1990A '%s' Time Read Mode\n", tag()); + + /* enable time counter */ + m_timer_clock->enable(1); + + /* load time counter data into shift register */ + for (int i = 0; i < 5; i++) + { + m_shift_reg[i] = m_time_counter[i]; + } + + if (LOG) logerror("uPD1990A '%s' Shift Register %02x%02x%02x%02x%02x\n", tag(), m_shift_reg[4], m_shift_reg[3], m_shift_reg[2], m_shift_reg[1], m_shift_reg[0]); + + /* 512 Hz data out pulse */ + m_data_out = 1; + m_timer_data_out->adjust(attotime::zero, 0, attotime::from_hz(512*2)); + + /* 32 Hz time pulse */ + m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(32*2)); + break; + + case MODE_TP_64HZ_SET: + if (LOG) logerror("uPD1990A '%s' TP = 64 Hz Set Mode\n", tag()); + + /* 64 Hz time pulse */ + m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(64*2)); + break; + + case MODE_TP_256HZ_SET: + if (LOG) logerror("uPD1990A '%s' TP = 256 Hz Set Mode\n", tag()); + + /* 256 Hz time pulse */ + m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(256*2)); + break; + + case MODE_TP_2048HZ_SET: + if (LOG) logerror("uPD1990A '%s' TP = 2048 Hz Set Mode\n", tag()); + + /* 2048 Hz time pulse */ + m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(2048*2)); + break; + + case MODE_TEST: + if (LOG) logerror("uPD1990A '%s' Test Mode not supported!\n", tag()); + + if (m_oe) + { + /* time counter is advanced at 1024 Hz from "Second" counter input */ + } + else + { + /* each counter is advanced at 1024 Hz in parallel, overflow carry does not affect next counter */ + } + + break; + } + } +} + + +//------------------------------------------------- +// clk_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::clk_w ) +{ + if (LOG) logerror("uPD1990A '%s' CLK %u\n", tag(), state); + + if (!m_clk && state) // rising edge + { + if (m_c == MODE_SHIFT) + { + m_shift_reg[0] >>= 1; + m_shift_reg[0] |= (BIT(m_shift_reg[1], 0) << 7); + + m_shift_reg[1] >>= 1; + m_shift_reg[1] |= (BIT(m_shift_reg[2], 0) << 7); + + m_shift_reg[2] >>= 1; + m_shift_reg[2] |= (BIT(m_shift_reg[3], 0) << 7); + + m_shift_reg[3] >>= 1; + m_shift_reg[3] |= (BIT(m_shift_reg[4], 0) << 7); + + m_shift_reg[4] >>= 1; + m_shift_reg[4] |= (m_data_in << 7); + + if (m_oe) + { + m_data_out = BIT(m_shift_reg[0], 0); + + if (LOG) logerror("uPD1990A '%s' DATA OUT %u\n", tag(), m_data_out); + + m_out_data_func(m_data_out); + } + } + } + + m_clk = state; +} + + +//------------------------------------------------- +// c0_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::c0_w ) +{ + if (LOG) logerror("uPD1990A '%s' C0 %u\n", tag(), state); + + m_c = (m_c & 0x06) | state; +} + + +//------------------------------------------------- +// c1_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::c1_w ) +{ + if (LOG) logerror("uPD1990A '%s' C1 %u\n", tag(), state); + + m_c = (m_c & 0x05) | (state << 1); +} + + +//------------------------------------------------- +// c2_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::c2_w ) +{ + if (LOG) logerror("uPD1990A '%s' C2 %u\n", tag(), state); + + m_c = (m_c & 0x03) | (state << 2); +} + + +//------------------------------------------------- +// data_in_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::data_in_w ) +{ + if (LOG) logerror("uPD1990A '%s' DATA IN %u\n", tag(), state); + + m_data_in = state; +} + + +//------------------------------------------------- +// data_out_r - +//------------------------------------------------- + +READ_LINE_MEMBER( upd1990a_device::data_out_r ) +{ + return m_data_out; +} + + +//------------------------------------------------- +// tp_r - +//------------------------------------------------- + +READ_LINE_MEMBER( upd1990a_device::tp_r ) +{ + return m_tp; +} |