diff options
Diffstat (limited to 'trunk/src/emu/machine/mccs1850.c')
-rw-r--r-- | trunk/src/emu/machine/mccs1850.c | 598 |
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; + } +} |