diff --git a/.gitattributes b/.gitattributes index 71411ead904..8361fa61cb2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -877,6 +877,8 @@ src/emu/machine/tmp68301.c svneol=native#text/plain src/emu/machine/tmp68301.h svneol=native#text/plain src/emu/machine/tms6100.c svneol=native#text/plain src/emu/machine/tms6100.h svneol=native#text/plain +src/emu/machine/upd1990a.c svneol=native#text/plain +src/emu/machine/upd1990a.h svneol=native#text/plain src/emu/machine/upd4701.c svneol=native#text/plain src/emu/machine/upd4701.h svneol=native#text/plain src/emu/machine/wd33c93.c svneol=native#text/plain diff --git a/src/emu/emu.mak b/src/emu/emu.mak index cbad586196b..40f3388d36c 100644 --- a/src/emu/emu.mak +++ b/src/emu/emu.mak @@ -213,6 +213,7 @@ EMUMACHINEOBJS = \ $(EMUMACHINE)/timekpr.o \ $(EMUMACHINE)/tmp68301.o \ $(EMUMACHINE)/tms6100.o \ + $(EMUMACHINE)/upd1990a.o \ $(EMUMACHINE)/upd4701.o \ $(EMUMACHINE)/wd33c93.o \ $(EMUMACHINE)/x2212.o \ diff --git a/src/emu/machine/upd1990a.c b/src/emu/machine/upd1990a.c new file mode 100644 index 00000000000..36c82d8fbf1 --- /dev/null +++ b/src/emu/machine/upd1990a.c @@ -0,0 +1,534 @@ +/********************************************************************** + + NEC uPD1990AC Serial I/O Calendar & Clock emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +/* + + TODO: + + - test mode + +*/ + +#include "emu.h" +#include "upd1990a.h" +#include "machine/devhelpr.h" + + + +//************************************************************************** +// 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, +}; + + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +// devices +const device_type UPD1990A = upd1990a_device_config::static_alloc_device_config; + + + +//************************************************************************** +// DEVICE CONFIGURATION +//************************************************************************** + +GENERIC_DEVICE_CONFIG_SETUP(upd1990a, "uPD1990A") + + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void upd1990a_device_config::device_config_complete() +{ + // inherit a copy of the static data + const upd1990a_interface *intf = reinterpret_cast(static_config()); + if (intf != NULL) + *static_cast(this) = *intf; + + // or initialize to defaults if none provided + else + { +// memset(&in_pa_func, 0, sizeof(in_pa_func)); + } +} + + + +//************************************************************************** +// 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(running_machine &_machine, const upd1990a_device_config &config) + : device_t(_machine, config), + m_config(config) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void upd1990a_device::device_start() +{ + // resolve callbacks + devcb_resolve_write_line(&m_out_data_func, &m_config.m_out_data_func, this); + devcb_resolve_write_line(&m_out_tp_func, &m_config.m_out_tp_func, this); + + // allocate timers + m_timer_clock = timer_alloc(TIMER_CLOCK); + m_timer_clock->adjust(attotime::zero, 0, attotime::from_hz(1)); + 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 TP %u\n", m_tp); + + devcb_call_write_line(&m_out_tp_func, m_tp); + break; + + case TIMER_DATA_OUT: + m_data_out = !m_data_out; + + if (LOG) logerror("UPD1990A DATA OUT TICK %u\n", m_data_out); + + devcb_call_write_line(&m_out_data_func, m_data_out); + break; + } +} + + +//------------------------------------------------- +// oe_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::oe_w ) +{ + if (LOG) logerror("UPD1990A OE %u\n", state); + + m_oe = state; +} + + +//------------------------------------------------- +// cs_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::cs_w ) +{ + if (LOG) logerror("UPD1990A CS %u\n", state); + + m_cs = state; +} + + +//------------------------------------------------- +// stb_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::stb_w ) +{ + if (LOG) logerror("UPD1990A STB %u\n", state); + + m_stb = state; + + if (m_cs && m_stb && !m_clk) + { + switch (m_c) + { + case MODE_REGISTER_HOLD: + if (LOG) logerror("UPD1990A Register Hold Mode\n"); + + /* 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 Shift Mode\n"); + + /* 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); + devcb_call_write_line(&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: + { + int i; + + if (LOG) logerror("UPD1990A Time Set Mode\n"); + if (LOG) logerror("UPD1990A Shift Register %02x%02x%02x%02x%02x\n", 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); + devcb_call_write_line(&m_out_data_func, m_data_out); + + /* load shift register data into time counter */ + for (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: + { + int i; + + if (LOG) logerror("UPD1990A Time Read Mode\n"); + + /* enable time counter */ + m_timer_clock->enable(1); + + /* load time counter data into shift register */ + for (i = 0; i < 5; i++) + { + m_shift_reg[i] = m_time_counter[i]; + } + + if (LOG) logerror("UPD1990A Shift Register %02x%02x%02x%02x%02x\n", 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 TP = 64 Hz Set Mode\n"); + + /* 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 TP = 256 Hz Set Mode\n"); + + /* 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 TP = 2048 Hz Set Mode\n"); + + /* 2048 Hz time pulse */ + m_timer_tp->adjust(attotime::zero, 0, attotime::from_hz(2048*2)); + break; + + case MODE_TEST: + if (LOG) logerror("UPD1990A Test Mode not supported!\n"); + + 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 CLK %u\n", 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 DATA OUT %u\n", m_data_out); + + devcb_call_write_line(&m_out_data_func, m_data_out); + } + } + } + + m_clk = state; +} + + +//------------------------------------------------- +// c0_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::c0_w ) +{ + if (LOG) logerror("UPD1990A C0 %u\n", state); + + m_c = (m_c & 0x06) | state; +} + + +//------------------------------------------------- +// c1_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::c1_w ) +{ + if (LOG) logerror("UPD1990A C1 %u\n", state); + + m_c = (m_c & 0x05) | (state << 1); +} + + +//------------------------------------------------- +// c2_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::c2_w ) +{ + if (LOG) logerror("UPD1990A C2 %u\n", state); + + m_c = (m_c & 0x03) | (state << 2); +} + + +//------------------------------------------------- +// data_in_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( upd1990a_device::data_in_w ) +{ + if (LOG) logerror("UPD1990A DATA IN %u\n", 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; +} diff --git a/src/emu/machine/upd1990a.h b/src/emu/machine/upd1990a.h new file mode 100644 index 00000000000..14fbaa8a55f --- /dev/null +++ b/src/emu/machine/upd1990a.h @@ -0,0 +1,150 @@ +/********************************************************************** + + NEC uPD1990AC Serial I/O Calendar & Clock emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +********************************************************************** + _____ _____ + C2 1 |* \_/ | 14 Vdd + C1 2 | | 13 XTAL + C0 3 | | 12 _XTAL + STB 4 | uPD1990AC | 11 OUT ENBL + CS 5 | | 10 TP + DATA IN 6 | | 9 DATA OUT + GND 7 |_____________| 8 CLK + +**********************************************************************/ + +#pragma once + +#ifndef __UPD1990A__ +#define __UPD1990A__ + +#include "emu.h" + + + +//************************************************************************** +// MACROS / CONSTANTS +//************************************************************************** + + + + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_UPD1990A_ADD(_tag, _clock, _config) \ + MCFG_DEVICE_ADD((_tag), UPD1990A, _clock) \ + MCFG_DEVICE_CONFIG(_config) + +#define UPD1990A_INTERFACE(name) \ + const upd1990a_interface (name) = + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> upd1990a_interface + +struct upd1990a_interface +{ + devcb_write_line m_out_data_func; + devcb_write_line m_out_tp_func; +}; + + + +// ======================> upd1990a_device_config + +class upd1990a_device_config : public device_config, + public upd1990a_interface +{ + friend class upd1990a_device; + + // construction/destruction + upd1990a_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock); + +public: + // allocators + static device_config *static_alloc_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock); + virtual device_t *alloc_device(running_machine &machine) const; + +protected: + // device_config overrides + virtual void device_config_complete(); +}; + + + +// ======================> upd1990a_device + +class upd1990a_device : public device_t +{ + friend class upd1990a_device_config; + + // construction/destruction + upd1990a_device(running_machine &_machine, const upd1990a_device_config &_config); + +public: + DECLARE_WRITE_LINE_MEMBER( oe_w ); + DECLARE_WRITE_LINE_MEMBER( cs_w ); + DECLARE_WRITE_LINE_MEMBER( stb_w ); + DECLARE_WRITE_LINE_MEMBER( clk_w ); + DECLARE_WRITE_LINE_MEMBER( c0_w ); + DECLARE_WRITE_LINE_MEMBER( c1_w ); + DECLARE_WRITE_LINE_MEMBER( c2_w ); + DECLARE_WRITE_LINE_MEMBER( data_in_w ); + DECLARE_READ_LINE_MEMBER( data_out_r ); + DECLARE_READ_LINE_MEMBER( tp_r ); + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + +private: + inline UINT8 convert_to_bcd(int val); + inline int bcd_to_integer(UINT8 val); + inline void advance_seconds(); + + static const device_timer_id TIMER_CLOCK = 0; + static const device_timer_id TIMER_TP = 1; + static const device_timer_id TIMER_DATA_OUT = 2; + + devcb_resolved_write_line m_out_data_func; + devcb_resolved_write_line m_out_tp_func; + + UINT8 m_time_counter[5]; // time counter + UINT8 m_shift_reg[5]; // shift register + + int m_oe; // output enable + int m_cs; // chip select + int m_stb; // strobe + int m_data_in; // data in + int m_data_out; // data out + int m_c; // command + int m_clk; // shift clock + int m_tp; // time pulse + + // timers + emu_timer *m_timer_clock; + emu_timer *m_timer_tp; + emu_timer *m_timer_data_out; + + const upd1990a_device_config &m_config; +}; + + +// device type definition +extern const device_type UPD1990A; + + + +#endif