Added MCCS1850 RTC emulation. [Curt Coder]

This commit is contained in:
Curt Coder 2011-05-01 17:58:05 +00:00
parent 41f86102e9
commit 83c16c9766
4 changed files with 722 additions and 0 deletions

2
.gitattributes vendored
View File

@ -837,6 +837,8 @@ src/emu/machine/mc6852.c svneol=native#text/plain
src/emu/machine/mc6852.h svneol=native#text/plain
src/emu/machine/mc68901.c svneol=native#text/plain
src/emu/machine/mc68901.h svneol=native#text/plain
src/emu/machine/mccs1850.c svneol=native#text/plain
src/emu/machine/mccs1850.h svneol=native#text/plain
src/emu/machine/microtch.c svneol=native#text/plain
src/emu/machine/microtch.h svneol=native#text/plain
src/emu/machine/mm74c922.c svneol=native#text/plain

View File

@ -194,6 +194,7 @@ EMUMACHINEOBJS = \
$(EMUMACHINE)/mc146818.o \
$(EMUMACHINE)/mc6852.o \
$(EMUMACHINE)/mc68901.o \
$(EMUMACHINE)/mccs1850.o \
$(EMUMACHINE)/mm74c922.o \
$(EMUMACHINE)/microtch.o \
$(EMUMACHINE)/mos6529.o \

584
src/emu/machine/mccs1850.c Normal file
View File

@ -0,0 +1,584 @@
/**********************************************************************
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;
}
devcb_call_write_line(&m_out_int_func, interrupt ? ASSERT_LINE : CLEAR_LINE);
}
//-------------------------------------------------
// set_pse_line -
//-------------------------------------------------
inline void mccs1850_device::set_pse_line(int state)
{
m_pse = state;
devcb_call_write_line(&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(0),
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
devcb_resolve_write_line(&m_out_int_func, &m_out_int_cb, this);
devcb_resolve_write_line(&m_out_pse_func, &m_out_pse_cb, this);
devcb_resolve_write_line(&m_out_nuc_func, &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()
{
memset(m_ram, 0xff, RAM_SIZE);
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);
}
//-------------------------------------------------
// 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) ? "Read" : "Write", 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 )
{
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;
}
}

135
src/emu/machine/mccs1850.h Normal file
View File

@ -0,0 +1,135 @@
/**********************************************************************
Motorola MCCS1850 Serial Real-Time Clock emulation
Copyright MESS Team.
Visit http://mamedev.org for licensing and usage restrictions.
**********************************************************************
_____ _____
Vbat 1 |* \_/ | 16 Vdd
_POR 2 | | 15 TEST
_INT 3 | | 14 XTAL1
SCK 4 | MCCS1850 | 13 XTAL2
SDI 5 | | 12 _PWRSW
SDO 6 | | 11 NUC
CE 7 | | 10 _PSE
Vss 8 |_____________| 9 PSE
**********************************************************************/
#pragma once
#ifndef __MCCS1850__
#define __MCCS1850__
#include "emu.h"
//**************************************************************************
// INTERFACE CONFIGURATION MACROS
//**************************************************************************
#define MCFG_MCCS1850_ADD(_tag, _clock, _config) \
MCFG_DEVICE_ADD(_tag, MCCS1850, _clock) \
MCFG_DEVICE_CONFIG(_config)
#define MCCS1850_INTERFACE(name) \
const mccs1850_interface (name) =
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> mccs1850_interface
struct mccs1850_interface
{
devcb_write_line m_out_int_cb;
devcb_write_line m_out_pse_cb;
devcb_write_line m_out_nuc_cb;
};
// ======================> mccs1850_device
class mccs1850_device : public device_t,
public device_rtc_interface,
public device_nvram_interface,
public mccs1850_interface
{
public:
// construction/destruction
mccs1850_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
DECLARE_WRITE_LINE_MEMBER( ce_w );
DECLARE_WRITE_LINE_MEMBER( sck_w );
DECLARE_READ_LINE_MEMBER( sdo_r );
DECLARE_WRITE_LINE_MEMBER( sdi_w );
DECLARE_WRITE_LINE_MEMBER( pwrsw_w );
DECLARE_WRITE_LINE_MEMBER( por_w );
DECLARE_WRITE_LINE_MEMBER( test_w );
protected:
// device-level overrides
virtual void device_config_complete();
virtual void device_start();
virtual void device_reset();
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
// device_rtc_interface overrides
virtual void rtc_set_time(int year, int month, int day, int day_of_week, int hour, int minute, int second);
virtual bool rtc_is_year_2000_compliant() { return false; }
// device_nvram_interface overrides
virtual void nvram_default();
virtual void nvram_read(emu_file &file);
virtual void nvram_write(emu_file &file);
private:
inline void check_interrupt();
inline void set_pse_line(int state);
inline UINT8 read_register(offs_t offset);
inline void write_register(offs_t offset, UINT8 data);
inline void advance_seconds();
static const device_timer_id TIMER_CLOCK = 0;
devcb_resolved_write_line m_out_int_func;
devcb_resolved_write_line m_out_pse_func;
devcb_resolved_write_line m_out_nuc_func;
UINT8 m_ram[0x80]; // RAM
// power supply
int m_pse; // power supply enable
// counter
UINT32 m_counter; // seconds counter
// serial interface
int m_ce; // chip enable
int m_sck; // serial clock
int m_sdo; // serial data out
int m_sdi; // serial data in
int m_state; // serial interface state
UINT8 m_address; // address counter
int m_bits; // bit counter
UINT8 m_shift; // shift register
// timers
emu_timer *m_clock_timer;
};
// device type definition
extern const device_type MCCS1850;
#endif