diff options
author | Nathan Woods <npwoods@mess.org> | 2012-12-15 18:37:52 +0000 |
---|---|---|
committer | Nathan Woods <npwoods@mess.org> | 2012-12-15 18:37:52 +0000 |
commit | bdab37ea7e30b2d9f22834e5b88c8cf9310be50f (patch) | |
tree | dce0b9944f14f7dc41308aee9d884971aa2ef22d | |
parent | 7a9891c9f67de1edd58d84e2a485b9656f70f150 (diff) |
[MSM6242] Modernized the MSM6242 RTC code. Timer now only fires when it needs
to (i.e. - trigger an interrupt). This should result in performance gains for
games/systems using this code.
-rw-r--r-- | src/emu/dirtc.c | 16 | ||||
-rw-r--r-- | src/emu/dirtc.h | 1 | ||||
-rw-r--r-- | src/emu/machine/msm6242.c | 453 | ||||
-rw-r--r-- | src/emu/machine/msm6242.h | 21 |
4 files changed, 353 insertions, 138 deletions
diff --git a/src/emu/dirtc.c b/src/emu/dirtc.c index c230f1eb856..38ad08ab573 100644 --- a/src/emu/dirtc.c +++ b/src/emu/dirtc.c @@ -157,7 +157,7 @@ void device_rtc_interface::advance_seconds() //------------------------------------------------- -// advance_clock - +// advance_minutes //------------------------------------------------- void device_rtc_interface::advance_minutes() @@ -173,9 +173,19 @@ void device_rtc_interface::advance_minutes() if (m_register[RTC_HOUR] == 24) { m_register[RTC_HOUR] = 0; - m_register[RTC_DAY]++; - m_register[RTC_DAY_OF_WEEK]++; + advance_days(); } +} + + +//------------------------------------------------- +// advance_days +//------------------------------------------------- + +void device_rtc_interface::advance_days() +{ + m_register[RTC_DAY]++; + m_register[RTC_DAY_OF_WEEK]++; if (m_register[RTC_DAY_OF_WEEK] == 8) { diff --git a/src/emu/dirtc.h b/src/emu/dirtc.h index 9e3f53a1022..3273cac623a 100644 --- a/src/emu/dirtc.h +++ b/src/emu/dirtc.h @@ -62,6 +62,7 @@ protected: void advance_seconds(); void advance_minutes(); + void advance_days(); void adjust_seconds(); // derived class overrides diff --git a/src/emu/machine/msm6242.c b/src/emu/machine/msm6242.c index aabecc97324..8214978d637 100644 --- a/src/emu/machine/msm6242.c +++ b/src/emu/machine/msm6242.c @@ -41,6 +41,9 @@ enum #define TIMER_RTC_CALLBACK 1 +#define LOG_UNMAPPED 1 +#define LOG_IRQ 1 + //************************************************************************** @@ -69,82 +72,85 @@ msm6242_device::msm6242_device(const machine_config &mconfig, const char *tag, d //------------------------------------------------- -// rtc_timer_callback +// device_start - device-specific startup //------------------------------------------------- -void msm6242_device::rtc_timer_callback() +void msm6242_device::device_start() { - static const UINT8 dpm[12] = { 0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31 }; - int dpm_count; + const msm6242_interface *intf = reinterpret_cast<const msm6242_interface *>(static_config()); + if (intf != NULL) + m_res_out_int_func.resolve(intf->m_out_int_func, *this); - m_tick++; + // let's call the timer callback every tick + m_timer = timer_alloc(TIMER_RTC_CALLBACK); + m_timer->adjust(attotime::zero); - if(m_irq_flag == 1 && m_irq_type == 0 && ((m_tick % 0x200) == 0)) // 1/64 of second - { - if ( !m_res_out_int_func.isnull() ) - m_res_out_int_func( ASSERT_LINE ); - } + // get real time from system + set_current_time(machine()); - if(m_tick & 0x8000) // 32,768 KHz == 0x8000 ticks - { - int sec = get_clock_register(RTC_SECOND); - int minute = get_clock_register(RTC_MINUTE); - int hour = get_clock_register(RTC_HOUR); - int day = get_clock_register(RTC_DAY); - int month = get_clock_register(RTC_MONTH); - int weekday = get_clock_register(RTC_DAY_OF_WEEK); - int year = get_clock_register(RTC_YEAR); - - m_tick = 0; - sec++; - - if(m_irq_flag == 1 && m_irq_type == 1) // 1 second clock - if ( !m_res_out_int_func.isnull() ) - m_res_out_int_func(ASSERT_LINE); - - if(sec >= 60) - { - minute++; sec = 0; - if(m_irq_flag == 1 && m_irq_type == 2) // 1 minute clock - if ( !m_res_out_int_func.isnull() ) - m_res_out_int_func(ASSERT_LINE); - } - if(minute >= 60) - { - hour++; minute = 0; - if(m_irq_flag == 1 && m_irq_type == 3) // 1 hour clock - if ( !m_res_out_int_func.isnull() ) - m_res_out_int_func(ASSERT_LINE); - } - if(hour >= 24) { day++; weekday++; hour = 0; } - if(weekday >= 6) { weekday = 1; } + // set up registers + m_tick = 0; + m_irq_flag = 0; + m_irq_type = 0; - /* TODO: crude leap year support */ - dpm_count = (month)-1; + // TODO: skns writes 0x4 to D then expects E == 6 and F == 4, perhaps those are actually saved in the RTC CMOS? + m_reg[0] = 0; + m_reg[1] = 0x6; + m_reg[2] = 0x4; - if(((year % 4) == 0) && month == 2) - { - if((day) >= dpm[dpm_count]+1+1) - { month++; day = 0x01; } - } - else if(day >= dpm[dpm_count]+1) { month++; day = 0x01; } - if(month >= 0x13) { year++; month = 1; } - if(year >= 100) { year = 0; } //1900-1999 possible timeframe - - set_clock_register(RTC_SECOND, sec); - set_clock_register(RTC_MINUTE, minute); - set_clock_register(RTC_HOUR, hour); - set_clock_register(RTC_DAY, day); - set_clock_register(RTC_MONTH, month); - set_clock_register(RTC_DAY_OF_WEEK, weekday); - set_clock_register(RTC_YEAR, year); - } + // save states + save_item(NAME(m_reg)); + save_item(NAME(m_irq_flag)); + save_item(NAME(m_irq_type)); + save_item(NAME(m_tick)); } //------------------------------------------------- -// device_timer - handle timer callbacks +// device_reset - device-specific reset +//------------------------------------------------- + +void msm6242_device::device_reset() +{ + if (!m_res_out_int_func.isnull()) + m_res_out_int_func(CLEAR_LINE); +} + + + +//------------------------------------------------- +// device_pre_save - called prior to saving the +// state, so that registered variables can be +// properly normalized +//------------------------------------------------- + +void msm6242_device::device_pre_save() +{ + // update the RTC registers so that we can get the right values + update_rtc_registers(); +} + + + +//------------------------------------------------- +// device_post_load - called after the loading a +// saved state, so that registered variables can +// be expaneded as necessary +//------------------------------------------------- + +void msm6242_device::device_post_load() +{ + // this is probably redundant, because the timer state is saved; but it isn't + // a terribly bad idea + update_timer(); +} + + + +//------------------------------------------------- +// device_timer - called whenever a device timer +// fires //------------------------------------------------- void msm6242_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) @@ -160,49 +166,195 @@ void msm6242_device::device_timer(emu_timer &timer, device_timer_id id, int para //------------------------------------------------- -// device_start - device-specific startup +// irq //------------------------------------------------- -void msm6242_device::device_start() +void msm6242_device::irq(UINT8 irq_type) { - const msm6242_interface *intf = reinterpret_cast<const msm6242_interface *>(static_config()); - if (intf != NULL) - m_res_out_int_func.resolve(intf->m_out_int_func, *this); + // are we actually raising this particular IRQ? + if (m_irq_flag == 1 && m_irq_type == irq_type) + { + // log if appropriate + if (LOG_IRQ) + logerror("%s: MSM6242 logging IRQ #%d\n", machine().describe_context(), (int) irq_type); - // let's call the timer callback every tick - m_timer = timer_alloc(TIMER_RTC_CALLBACK); - m_timer->adjust(attotime::zero, 0, attotime::from_hz(clock())); + // ...and assert the output line + if (!m_res_out_int_func.isnull()) + m_res_out_int_func(ASSERT_LINE); + } +} - // get real time from system - set_current_time(machine()); - // set up registers - m_tick = 0; - m_irq_flag = 0; - m_irq_type = 0; - // TODO: skns writes 0x4 to D then expects E == 6 and F == 4, perhaps those are actually saved in the RTC CMOS? - m_reg[0] = 0; - m_reg[1] = 0x6; - m_reg[2] = 0x4; +//------------------------------------------------- +// bump +//------------------------------------------------- - // save states - save_item(NAME(m_reg)); - save_item(NAME(m_irq_flag)); - save_item(NAME(m_irq_type)); - save_item(NAME(m_tick)); +UINT64 msm6242_device::bump(int rtc_register, UINT64 delta, UINT64 register_min, UINT64 register_range) +{ + UINT64 carry = 0; + + if (delta > 0) + { + // get the register value + UINT64 register_value = (rtc_register == RTC_TICKS) + ? m_tick + : get_clock_register(rtc_register); + + // increment the value + UINT64 new_register_value = ((register_value - register_min + delta) % register_range) + register_min; + + // calculate the cary + carry = ((register_value - register_min) + delta) / register_range; + + // store the new register value + if (rtc_register == RTC_TICKS) + m_tick = (UINT16) new_register_value; + else + set_clock_register(rtc_register, (int) new_register_value); + } + + return carry; } //------------------------------------------------- -// device_reset - device-specific reset +// current_time //------------------------------------------------- -void msm6242_device::device_reset() +UINT64 msm6242_device::current_time() +{ + return machine().time().as_ticks(clock()); +} + + + +//------------------------------------------------- +// update_rtc_registers +//------------------------------------------------- + +void msm6242_device::update_rtc_registers() { - if ( !m_res_out_int_func.isnull() ) - m_res_out_int_func( CLEAR_LINE ); + // get the absolute current time, in ticks + UINT64 curtime = current_time(); + + // how long as it been since we last updated? + UINT64 delta = curtime - m_last_update_time; + + // set current time + m_last_update_time = curtime; + + // no delta? just return + if (delta <= 0) + return; + + // ticks + if ((m_tick % 200) != ((delta + m_tick) % 0x200)) + irq(IRQ_64THSECOND); + delta = bump(RTC_TICKS, delta, 0, 0x8000); + if (delta <= 0) + return; + + // seconds + irq(IRQ_SECOND); + delta = bump(RTC_SECOND, delta, 0, 60); + if (delta <= 0) + return; + + // minutes + irq(IRQ_MINUTE); + delta = bump(RTC_MINUTE, delta, 0, 60); + if (delta <= 0) + return; + + // hours + irq(IRQ_HOUR); + delta = bump(RTC_HOUR, delta, 0, 24); + if (delta <= 0) + return; + + // days + while(delta--) + advance_days(); +} + + + +//------------------------------------------------- +// update_timer +//------------------------------------------------- + +void msm6242_device::update_timer() +{ + UINT64 callback_ticks = 0; + attotime callback_time = attotime::never; + + // we only need to call back if the IRQ flag is on, and we have a handler + if (!m_res_out_int_func.isnull() && m_irq_flag == 1) + { + switch(m_irq_type) + { + case IRQ_HOUR: + callback_ticks += (59 - get_clock_register(RTC_MINUTE)) * (0x8000 * 60); + // fall through + + case IRQ_MINUTE: + callback_ticks += (59 - get_clock_register(RTC_SECOND)) * 0x8000; + // fall through + + case IRQ_SECOND: + callback_ticks += 0x8000 - m_tick; + break; + + case IRQ_64THSECOND: + callback_ticks += 0x200 - (m_tick % 0x200); + break; + } + } + + // if set, convert ticks to an attotime + if (callback_ticks > 0) + { + callback_time = attotime::from_ticks(callback_ticks, clock()) - machine().time(); + } + + m_timer->adjust(callback_time); +} + + + +//------------------------------------------------- +// rtc_clock_updated +//------------------------------------------------- + +void msm6242_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) +{ + m_last_update_time = current_time(); +} + + + +//------------------------------------------------- +// rtc_timer_callback +//------------------------------------------------- + +void msm6242_device::rtc_timer_callback() +{ + update_rtc_registers(); +} + + + +//------------------------------------------------- +// get_clock_nibble +//------------------------------------------------- + +UINT8 msm6242_device::get_clock_nibble(int rtc_register, bool high) +{ + int value = get_clock_register(rtc_register); + value /= high ? 10 : 1; + return (UINT8) ((value % 10) & 0x0F); } @@ -217,27 +369,37 @@ void msm6242_device::device_reset() READ8_MEMBER( msm6242_device::read ) { - int sec = get_clock_register(RTC_SECOND); - int minute = get_clock_register(RTC_MINUTE); - int hour = get_clock_register(RTC_HOUR); - int day = get_clock_register(RTC_DAY); - int month = get_clock_register(RTC_MONTH); - int weekday = get_clock_register(RTC_DAY_OF_WEEK); - int year = get_clock_register(RTC_YEAR); + int hour, pm; + UINT8 result; + + // update the registers; they may have changed + update_rtc_registers(); switch(offset) { - case MSM6242_REG_S1: return (sec % 10) & 0xf; - case MSM6242_REG_S10: return (sec / 10) & 0xf; - case MSM6242_REG_MI1: return (minute % 10) & 0xf; - case MSM6242_REG_MI10: return (minute / 10) & 0xf; + case MSM6242_REG_S1: + result = get_clock_nibble(RTC_SECOND, false); + break; + + case MSM6242_REG_S10: + result = get_clock_nibble(RTC_SECOND, true); + break; + + case MSM6242_REG_MI1: + result = get_clock_nibble(RTC_MINUTE, false); + break; + + case MSM6242_REG_MI10: + result = get_clock_nibble(RTC_MINUTE, true); + break; + case MSM6242_REG_H1: case MSM6242_REG_H10: - { - int pm = 0; + pm = 0; + hour = get_clock_register(RTC_HOUR); - /* check for 12/24 hour mode */ - if ((m_reg[2] & 0x04) == 0) /* 12 hour mode? */ + // check for 12/24 hour mode + if ((m_reg[2] & 0x04) == 0) // 12 hour mode? { if (hour >= 12) pm = 1; @@ -249,25 +411,53 @@ READ8_MEMBER( msm6242_device::read ) } if ( offset == MSM6242_REG_H1 ) - return hour % 10; + result = hour % 10; + else + result = (hour / 10) | (pm <<2); + break; - return (hour / 10) | (pm <<2); - } + case MSM6242_REG_D1: + result = get_clock_nibble(RTC_DAY, false); + break; + + case MSM6242_REG_D10: + result = get_clock_nibble(RTC_DAY, true); + break; + + case MSM6242_REG_MO1: + result = get_clock_nibble(RTC_MONTH, false); + break; + + case MSM6242_REG_MO10: + result = get_clock_nibble(RTC_MONTH, true); + break; - case MSM6242_REG_D1: return (day % 10) & 0xf; - case MSM6242_REG_D10: return (day / 10) & 0xf; - case MSM6242_REG_MO1: return (month % 10) & 0xf; - case MSM6242_REG_MO10: return (month / 10) & 0xf; - case MSM6242_REG_Y1: return (year % 10) & 0xf; - case MSM6242_REG_Y10: return ((year / 10) % 10) & 0xf; - case MSM6242_REG_W: return weekday; - case MSM6242_REG_CD: return m_reg[0]; - case MSM6242_REG_CE: return m_reg[1]; - case MSM6242_REG_CF: return m_reg[2]; + case MSM6242_REG_Y1: + result = get_clock_nibble(RTC_YEAR, false); + break; + + case MSM6242_REG_Y10: + result = get_clock_nibble(RTC_YEAR, true); + break; + + case MSM6242_REG_W: + result = (UINT8) (get_clock_register(RTC_DAY_OF_WEEK) - 1); + break; + + case MSM6242_REG_CD: + case MSM6242_REG_CE: + case MSM6242_REG_CF: + result = m_reg[offset - MSM6242_REG_CD]; + break; + + default: + result = 0x00; + if (LOG_UNMAPPED) + logerror("%s: MSM6242 unmapped offset %02x read\n", machine().describe_context(), offset); + break; } - logerror("%s: MSM6242 unmapped offset %02x read\n", machine().describe_context(), offset); - return 0; + return result; } @@ -281,23 +471,17 @@ WRITE8_MEMBER( msm6242_device::write ) switch(offset) { case MSM6242_REG_CD: - { // x--- 30s ADJ // -x-- IRQ FLAG // --x- BUSY // ---x HOLD - m_reg[0] = data & 0x0f; - - return; - } + break; case MSM6242_REG_CE: - { // xx-- t0,t1 (timing irq) // --x- STD // ---x MASK - m_reg[1] = data & 0x0f; if((data & 3) == 0) // MASK & STD = 0 { @@ -310,12 +494,9 @@ WRITE8_MEMBER( msm6242_device::write ) if ( !m_res_out_int_func.isnull() ) m_res_out_int_func( CLEAR_LINE ); } - - return; - } + break; case MSM6242_REG_CF: - { // x--- TEST // -x-- 24/12 // --x- STOP @@ -326,10 +507,14 @@ WRITE8_MEMBER( msm6242_device::write ) m_reg[2] = (m_reg[2] & ~0x04) | (data & 0x04); else m_reg[2] = (data & 0x0b) | (m_reg[2] & 4); + break; - return; - } + default: + if (LOG_UNMAPPED) + logerror("%s: MSM6242 unmapped offset %02x written with %02x\n", machine().describe_context(), offset, data); + break; } - logerror("%s: MSM6242 unmapped offset %02x written with %02x\n", machine().describe_context(), offset, data); + // update the timer variable in response to potential changes + update_timer(); } diff --git a/src/emu/machine/msm6242.h b/src/emu/machine/msm6242.h index 3c95cca6ecc..db52a7653de 100644 --- a/src/emu/machine/msm6242.h +++ b/src/emu/machine/msm6242.h @@ -47,9 +47,21 @@ protected: // device-level overrides virtual void device_start(); virtual void device_reset(); + virtual void device_pre_save(); + virtual void device_post_load(); virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + // rtc overrides + virtual void rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second); + private: + static const int RTC_TICKS = ~0; + + static const UINT8 IRQ_64THSECOND = 0; + static const UINT8 IRQ_SECOND = 1; + static const UINT8 IRQ_MINUTE = 2; + static const UINT8 IRQ_HOUR = 3; + // state UINT8 m_reg[3]; UINT8 m_irq_flag; @@ -59,9 +71,16 @@ private: // incidentals devcb_resolved_write_line m_res_out_int_func; emu_timer * m_timer; + UINT64 m_last_update_time; // last update time, in clock cycles - // callbacks + // methods void rtc_timer_callback(); + UINT64 current_time(); + void irq(UINT8 irq_type); + UINT64 bump(int rtc_register, UINT64 delta, UINT64 register_min, UINT64 register_range); + void update_rtc_registers(); + void update_timer(); + UINT8 get_clock_nibble(int rtc_register, bool high); }; |