summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/mccs1850.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/mccs1850.c')
-rw-r--r--trunk/src/emu/machine/mccs1850.c598
1 files changed, 598 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/mccs1850.c b/trunk/src/emu/machine/mccs1850.c
new file mode 100644
index 00000000000..38d446d577c
--- /dev/null
+++ b/trunk/src/emu/machine/mccs1850.c
@@ -0,0 +1,598 @@
+/**********************************************************************
+
+ Motorola MCCS1850 Serial Real-Time Clock emulation
+
+ Copyright MESS Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+*********************************************************************/
+
+/*
+
+ TODO:
+
+ - auto restart
+ - test mode
+
+*/
+
+#include "mccs1850.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+#define RAM_SIZE 0x80
+
+
+// serial state
+enum
+{
+ STATE_ADDRESS,
+ STATE_DATA
+};
+
+
+// registers
+enum
+{
+ REGISTER_COUNTER_LATCH = 0x20,
+ REGISTER_ALARM_LATCH = 0x24,
+ REGISTER_STATUS = 0x30,
+ REGISTER_CONTROL = 0x31,
+ REGISTER_TEST_1 = 0x3e,
+ REGISTER_TEST_2 = 0x3f,
+ REGISTER_TEST_KICK_START_COUNTER = 0x40,
+ REGISTER_TEST_PRESCALE_COUNTER = 0x43,
+ REGISTER_TEST_COUNTER_INCREMENT = 0x4f
+};
+
+
+// clock status/interrupt register
+#define STATUS_TM 0x20 // test mode
+#define STATUS_FTU 0x10 // first time up
+#define STATUS_IT 0x08 // interrupt true
+#define STATUS_LB 0x04 // low battery
+#define STATUS_AI 0x02 // alarm
+#define STATUS_RPD 0x01 // request to power down
+
+
+// clock control register
+#define CONTROL_STR_STP 0x80 // start/stop
+#define CONTROL_PD 0x40 // power down
+#define CONTROL_AR 0x20 // auto restart
+#define CONTROL_AE 0x10 // alarm enable
+#define CONTROL_AC 0x08 // alarm clear
+#define CONTROL_FTUC 0x04 // first time up clear
+#define CONTROL_LBE 0x02 // low battery enable
+#define CONTROL_RPCD 0x01 // request to power down clear
+
+
+// test register 1
+#define TEST1_DIV1 0x80 // divide by 1
+#define TEST1_VOVR 0x40 // Vdd override
+#define TEST1_VDDUP 0x20 // Vdd up
+#define TEST1_VDDON 0x10 // Vdd on
+#define TEST1_VRT 0x08 // valid RAM and time
+#define TEST1_LOW_BAT 0x08 // low battery
+#define TEST1_PCC 0x04 // programmable capacitor C (10.0 pF)
+#define TEST1_PCB 0x02 // programmable capacitor B (5.0 pF)
+#define TEST1_PCA 0x01 // programmable capacitor A (2.5 pF)
+
+
+// test register 2
+#define TEST2_OSCBY 0x80 // oscillator bypass
+#define TEST2_COMPOVR 0x40 // comparator override
+#define TEST2_POR 0x20 // power on reset
+#define TEST2_SELTCK 0x10 // select test clock
+#define TEST2_FRZ 0x08 // freeze mode
+#define TEST2_DV_MASK 0x07 // divider bits select
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+const device_type MCCS1850 = &device_creator<mccs1850_device>;
+
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// check_interrupt -
+//-------------------------------------------------
+
+inline void mccs1850_device::check_interrupt()
+{
+ UINT8 status = m_ram[REGISTER_STATUS];
+ UINT8 control = m_ram[REGISTER_CONTROL];
+
+ bool interrupt = (((status & STATUS_AI) && (control & CONTROL_AE)) // alarm interrupt
+ || ((status & STATUS_LB) && (control & CONTROL_LBE)) // low battery
+ || (status & STATUS_FTU) // first time up
+ || (status & STATUS_RPD)); // request to power down
+
+ if (interrupt)
+ {
+ m_ram[REGISTER_STATUS] |= STATUS_IT;
+ }
+ else
+ {
+ m_ram[REGISTER_STATUS] &= ~STATUS_IT;
+ }
+
+ m_out_int_func(interrupt ? ASSERT_LINE : CLEAR_LINE);
+}
+
+
+//-------------------------------------------------
+// set_pse_line -
+//-------------------------------------------------
+
+inline void mccs1850_device::set_pse_line(int state)
+{
+ m_pse = state;
+
+ m_out_pse_func(m_pse);
+}
+
+
+//-------------------------------------------------
+// read_register -
+//-------------------------------------------------
+
+inline UINT8 mccs1850_device::read_register(offs_t offset)
+{
+ switch (offset)
+ {
+ case REGISTER_COUNTER_LATCH:
+ // load counter value into latch
+ m_ram[REGISTER_COUNTER_LATCH] = m_counter >> 24;
+ m_ram[REGISTER_COUNTER_LATCH + 1] = m_counter >> 16;
+ m_ram[REGISTER_COUNTER_LATCH + 2] = m_counter >> 8;
+ m_ram[REGISTER_COUNTER_LATCH + 3] = m_counter;
+ break;
+
+ case REGISTER_TEST_1:
+ case REGISTER_TEST_2:
+ case REGISTER_TEST_KICK_START_COUNTER:
+ case REGISTER_TEST_PRESCALE_COUNTER:
+ case REGISTER_TEST_COUNTER_INCREMENT:
+ logerror("MCCS1850 '%s' Unsupported read from test register %02x!\n", tag(), offset);
+ break;
+ }
+
+ return m_ram[offset];
+}
+
+
+//-------------------------------------------------
+// write_register -
+//-------------------------------------------------
+
+inline void mccs1850_device::write_register(offs_t offset, UINT8 data)
+{
+ switch (offset)
+ {
+ case REGISTER_STATUS:
+ // read only
+ break;
+
+ case REGISTER_CONTROL:
+ if (LOG) logerror("MCCS1850 '%s' Counter %s\n", tag(), (data & CONTROL_STR_STP) ? "Start" : "Stop");
+ m_clock_timer->enable(data & CONTROL_STR_STP);
+
+ if (data & CONTROL_PD)
+ {
+ if (LOG) logerror("MCCS1850 '%s' Power Down\n", tag());
+ set_pse_line(0);
+ }
+
+ if (data & CONTROL_AR)
+ {
+ if (LOG) logerror("MCCS1850 '%s' Auto Restart\n", tag());
+ }
+
+ if (data & CONTROL_AC)
+ {
+ if (LOG) logerror("MCCS1850 '%s' Alarm Clear\n", tag());
+ m_ram[REGISTER_STATUS] &= ~STATUS_AI;
+ }
+
+ if (data & CONTROL_FTUC)
+ {
+ if (LOG) logerror("MCCS1850 '%s' First Time Up Clear\n", tag());
+ m_ram[REGISTER_STATUS] &= ~STATUS_FTU;
+ }
+
+ if (data & CONTROL_RPCD)
+ {
+ if (LOG) logerror("MCCS1850 '%s' Request to Power Down Clear\n", tag());
+ m_ram[REGISTER_STATUS] &= ~STATUS_RPD;
+ }
+
+ m_ram[REGISTER_CONTROL] = data & 0xb2;
+
+ check_interrupt();
+ break;
+
+ case REGISTER_TEST_1:
+ case REGISTER_TEST_2:
+ case REGISTER_TEST_KICK_START_COUNTER:
+ case REGISTER_TEST_PRESCALE_COUNTER:
+ case REGISTER_TEST_COUNTER_INCREMENT:
+ logerror("MCCS1850 '%s' Unsupported write to test register %02x!\n", tag(), offset);
+ break;
+
+ default:
+ m_ram[offset] = data;
+ }
+}
+
+
+//-------------------------------------------------
+// advance_seconds -
+//-------------------------------------------------
+
+inline void mccs1850_device::advance_seconds()
+{
+ UINT32 alarm = (m_ram[REGISTER_ALARM_LATCH] << 24) | (m_ram[REGISTER_ALARM_LATCH + 1] << 16) | (m_ram[REGISTER_ALARM_LATCH + 2] << 8) | m_ram[REGISTER_ALARM_LATCH + 3];
+
+ m_counter++;
+
+ if (m_counter == alarm)
+ {
+ if (m_pse)
+ {
+ // trigger alarm
+ m_ram[REGISTER_STATUS] |= STATUS_AI;
+
+ check_interrupt();
+ }
+ else
+ {
+ // wake up
+ set_pse_line(1);
+ }
+ }
+}
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// mccs1850_device - constructor
+//-------------------------------------------------
+
+mccs1850_device::mccs1850_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MCCS1850, "MCCS1850", tag, owner, clock),
+ device_rtc_interface(mconfig, *this),
+ device_nvram_interface(mconfig, *this),
+ m_pse(1),
+ m_ce(0),
+ m_sck(0),
+ m_sdo(1),
+ m_sdi(0),
+ m_state(STATE_ADDRESS),
+ m_bits(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void mccs1850_device::device_config_complete()
+{
+ // inherit a copy of the static data
+ const mccs1850_interface *intf = reinterpret_cast<const mccs1850_interface *>(static_config());
+ if (intf != NULL)
+ *static_cast<mccs1850_interface *>(this) = *intf;
+
+ // or initialize to defaults if none provided
+ else
+ {
+ memset(&m_out_int_cb, 0, sizeof(m_out_int_cb));
+ memset(&m_out_pse_cb, 0, sizeof(m_out_pse_cb));
+ memset(&m_out_nuc_cb, 0, sizeof(m_out_nuc_cb));
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mccs1850_device::device_start()
+{
+ // resolve callbacks
+ m_out_int_func.resolve(m_out_int_cb, *this);
+ m_out_pse_func.resolve(m_out_pse_cb, *this);
+ m_out_nuc_func.resolve(m_out_nuc_cb, *this);
+
+ // allocate timers
+ m_clock_timer = timer_alloc(TIMER_CLOCK);
+ m_clock_timer->adjust(attotime::from_hz(clock() / 32768), 0, attotime::from_hz(clock() / 32768));
+
+ // state saving
+ save_item(NAME(m_pse));
+ save_item(NAME(m_counter));
+ save_item(NAME(m_ce));
+ save_item(NAME(m_sck));
+ save_item(NAME(m_sdo));
+ save_item(NAME(m_sdi));
+ save_item(NAME(m_state));
+ save_item(NAME(m_address));
+ save_item(NAME(m_bits));
+ save_item(NAME(m_shift));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void mccs1850_device::device_reset()
+{
+ m_ram[REGISTER_STATUS] = 0x80 | STATUS_FTU;
+ m_ram[REGISTER_CONTROL] = 0x00;
+}
+
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+
+void mccs1850_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_CLOCK:
+ advance_seconds();
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// rtc_set_time - called to initialize the RTC to
+// a known state
+//-------------------------------------------------
+
+void mccs1850_device::rtc_set_time(int year, int month, int day, int day_of_week, int hour, int minute, int second)
+{
+}
+
+
+//-------------------------------------------------
+// nvram_default - called to initialize NVRAM to
+// its default state
+//-------------------------------------------------
+
+void mccs1850_device::nvram_default()
+{
+ memset(m_ram, 0xff, RAM_SIZE);
+
+ if (machine().region(tag()) != NULL)
+ {
+ UINT8 *nvram = machine().region(tag())->base();
+
+ // initialize NVRAM
+ memcpy(m_ram, nvram, 0x20);
+ }
+}
+
+
+//-------------------------------------------------
+// nvram_read - called to read NVRAM from the
+// .nv file
+//-------------------------------------------------
+
+void mccs1850_device::nvram_read(emu_file &file)
+{
+ file.read(m_ram, RAM_SIZE);
+}
+
+
+//-------------------------------------------------
+// nvram_write - called to write NVRAM to the
+// .nv file
+//-------------------------------------------------
+
+void mccs1850_device::nvram_write(emu_file &file)
+{
+ file.write(m_ram, RAM_SIZE);
+}
+
+
+//-------------------------------------------------
+// ce_w - chip enable write
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( mccs1850_device::ce_w )
+{
+ m_ce = state;
+
+ if (!m_ce)
+ {
+ m_state = STATE_ADDRESS;
+ m_bits = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// sck_w - serial clock write
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( mccs1850_device::sck_w )
+{
+ if (!m_ce) return;
+
+ switch (m_state)
+ {
+ case STATE_ADDRESS:
+ if (m_sck && !state)
+ {
+ m_address <<= 1;
+ m_address |= m_sdi;
+ m_bits++;
+
+ if (m_bits == 8)
+ {
+ if (LOG) logerror("MCCS1850 '%s' %s Address %u\n", tag(), BIT(m_address, 7) ? "Write" : "Read", m_address & 0x7f);
+
+ m_bits = 0;
+ m_state = STATE_DATA;
+
+ if (!BIT(m_address, 7))
+ {
+ m_shift = read_register(m_address & 0x7f);
+
+ if (LOG) logerror("MCCS1850 '%s' Data Out %02x\n", tag(), m_shift);
+ }
+ }
+ }
+ break;
+
+ case STATE_DATA:
+ if (BIT(m_address, 7) && m_sck && !state)
+ {
+ // shift data in
+ m_shift <<= 1;
+ m_shift |= m_sdi;
+ m_bits++;
+
+ if (m_bits == 8)
+ {
+ if (LOG) logerror("MCCS1850 '%s' Data In %02x\n", tag(), m_shift);
+
+ write_register(m_address & 0x7f, m_shift);
+
+ m_bits = 0;
+
+ // increment address counter
+ m_address++;
+ m_address &= 0x7f;
+ }
+ }
+ else if (!BIT(m_address, 7) && !m_sck && state)
+ {
+ // shift data out
+ m_sdo = BIT(m_shift, 7);
+ m_shift <<= 1;
+ m_bits++;
+
+ if (m_bits == 8)
+ {
+ m_bits = 0;
+
+ // increment address counter
+ m_address++;
+ m_address &= 0x7f;
+ m_shift = read_register(m_address & 0x7f);
+ }
+ }
+ break;
+ }
+
+ m_sck = state;
+}
+
+
+//-------------------------------------------------
+// sdo_r - serial data out read
+//-------------------------------------------------
+
+READ_LINE_MEMBER( mccs1850_device::sdo_r )
+{
+ if (!m_ce || BIT(m_address, 7))
+ {
+ // Hi-Z
+ return 1;
+ }
+ else
+ {
+ return m_sdo;
+ }
+}
+
+
+//-------------------------------------------------
+// sdi_w - serial data in write
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( mccs1850_device::sdi_w )
+{
+ m_sdi = state;
+}
+
+
+//-------------------------------------------------
+// pwrsw_w - power switch write
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( mccs1850_device::pwrsw_w )
+{
+ if (!state)
+ {
+ if (m_pse)
+ {
+ // request to power down
+ m_ram[REGISTER_STATUS] |= STATUS_RPD;
+ check_interrupt();
+ }
+
+ set_pse_line(1);
+ }
+}
+
+
+//-------------------------------------------------
+// por_w - power on reset write
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( mccs1850_device::por_w )
+{
+ if (!state)
+ {
+ device_reset();
+ }
+}
+
+
+//-------------------------------------------------
+// test_w - test mode write
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( mccs1850_device::test_w )
+{
+ if (state)
+ {
+ if (LOG) logerror("MCCS1850 '%s' Test Mode\n", tag());
+
+ m_ram[REGISTER_STATUS] |= STATUS_TM;
+ }
+ else
+ {
+ if (LOG) logerror("MCCS1850 '%s' Normal Operation\n", tag());
+
+ m_ram[REGISTER_STATUS] &= ~STATUS_TM;
+ }
+}