Previous 199869 Revisions Next

r19574 Saturday 15th December, 2012 at 18:37:52 UTC by Nathan Woods
[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.
[src/emu]dirtc.c dirtc.h
[src/emu/machine]msm6242.c msm6242.h

trunk/src/emu/machine/msm6242.c
r19573r19574
4141
4242#define TIMER_RTC_CALLBACK      1
4343
44#define LOG_UNMAPPED         1
45#define LOG_IRQ               1
4446
4547
48
4649//**************************************************************************
4750//  GLOBAL VARIABLES
4851//**************************************************************************
r19573r19574
6972
7073
7174//-------------------------------------------------
72//  rtc_timer_callback
75//  device_start - device-specific startup
7376//-------------------------------------------------
7477
75void msm6242_device::rtc_timer_callback()
78void msm6242_device::device_start()
7679{
77   static const UINT8 dpm[12] = { 0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31 };
78   int dpm_count;
80   const msm6242_interface *intf = reinterpret_cast<const msm6242_interface *>(static_config());
81   if (intf != NULL)
82      m_res_out_int_func.resolve(intf->m_out_int_func, *this);
7983
80   m_tick++;
84   // let's call the timer callback every tick
85   m_timer = timer_alloc(TIMER_RTC_CALLBACK);
86   m_timer->adjust(attotime::zero);
8187
82   if(m_irq_flag == 1 && m_irq_type == 0 && ((m_tick % 0x200) == 0)) // 1/64 of second
83   {
84      if ( !m_res_out_int_func.isnull() )
85         m_res_out_int_func( ASSERT_LINE );
86   }
88   // get real time from system
89   set_current_time(machine());
8790
88   if(m_tick & 0x8000) // 32,768 KHz == 0x8000 ticks
89   {
90      int sec = get_clock_register(RTC_SECOND);
91      int minute = get_clock_register(RTC_MINUTE);
92      int hour = get_clock_register(RTC_HOUR);
93      int day = get_clock_register(RTC_DAY);
94      int month = get_clock_register(RTC_MONTH);
95      int weekday = get_clock_register(RTC_DAY_OF_WEEK);
96      int year = get_clock_register(RTC_YEAR);
91   // set up registers
92   m_tick = 0;
93   m_irq_flag = 0;
94   m_irq_type = 0;
9795
98      m_tick = 0;
99      sec++;
96   // TODO: skns writes 0x4 to D then expects E == 6 and F == 4, perhaps those are actually saved in the RTC CMOS?
97   m_reg[0] = 0;
98   m_reg[1] = 0x6;
99   m_reg[2] = 0x4;
100100
101      if(m_irq_flag == 1 && m_irq_type == 1) // 1 second clock
102         if ( !m_res_out_int_func.isnull() )
103            m_res_out_int_func(ASSERT_LINE);
101   // save states
102   save_item(NAME(m_reg));
103   save_item(NAME(m_irq_flag));
104   save_item(NAME(m_irq_type));
105   save_item(NAME(m_tick));
106}
104107
105      if(sec >= 60)
106      {
107         minute++; sec = 0;
108         if(m_irq_flag == 1 && m_irq_type == 2) // 1 minute clock
109            if ( !m_res_out_int_func.isnull() )
110               m_res_out_int_func(ASSERT_LINE);
111      }
112      if(minute >= 60)
113      {
114         hour++; minute = 0;
115         if(m_irq_flag == 1 && m_irq_type == 3) // 1 hour clock
116            if ( !m_res_out_int_func.isnull() )
117               m_res_out_int_func(ASSERT_LINE);
118      }
119      if(hour >= 24)         { day++; weekday++; hour = 0; }
120      if(weekday >= 6)            { weekday = 1; }
121108
122      /* TODO: crude leap year support */
123      dpm_count = (month)-1;
124109
125      if(((year % 4) == 0) && month == 2)
126      {
127         if((day) >= dpm[dpm_count]+1+1)
128            { month++; day = 0x01; }
129      }
130      else if(day >= dpm[dpm_count]+1)      { month++; day = 0x01; }
131      if(month >= 0x13)                  { year++; month = 1; }
132      if(year >= 100)                  { year = 0; } //1900-1999 possible timeframe
110//-------------------------------------------------
111//  device_reset - device-specific reset
112//-------------------------------------------------
133113
134      set_clock_register(RTC_SECOND, sec);
135      set_clock_register(RTC_MINUTE, minute);
136      set_clock_register(RTC_HOUR, hour);
137      set_clock_register(RTC_DAY, day);
138      set_clock_register(RTC_MONTH, month);
139      set_clock_register(RTC_DAY_OF_WEEK, weekday);
140      set_clock_register(RTC_YEAR, year);
141   }
114void msm6242_device::device_reset()
115{
116   if (!m_res_out_int_func.isnull())
117      m_res_out_int_func(CLEAR_LINE);
142118}
143119
144120
145121
146122//-------------------------------------------------
147//  device_timer - handle timer callbacks
123//  device_pre_save - called prior to saving the
124//  state, so that registered variables can be
125//  properly normalized
148126//-------------------------------------------------
149127
128void msm6242_device::device_pre_save()
129{
130   // update the RTC registers so that we can get the right values
131   update_rtc_registers();
132}
133
134
135
136//-------------------------------------------------
137//  device_post_load - called after the loading a
138//  saved state, so that registered variables can
139//  be expaneded as necessary
140//-------------------------------------------------
141
142void msm6242_device::device_post_load()
143{
144   // this is probably redundant, because the timer state is saved; but it isn't
145   // a terribly bad idea
146   update_timer();
147}
148
149
150
151//-------------------------------------------------
152//  device_timer - called whenever a device timer
153//  fires
154//-------------------------------------------------
155
150156void msm6242_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
151157{
152158   switch(id)
r19573r19574
160166
161167
162168//-------------------------------------------------
163//  device_start - device-specific startup
169//  irq
164170//-------------------------------------------------
165171
166void msm6242_device::device_start()
172void msm6242_device::irq(UINT8 irq_type)
167173{
168   const msm6242_interface *intf = reinterpret_cast<const msm6242_interface *>(static_config());
169   if (intf != NULL)
170      m_res_out_int_func.resolve(intf->m_out_int_func, *this);
174   // are we actually raising this particular IRQ?
175   if (m_irq_flag == 1 && m_irq_type == irq_type)
176   {
177      // log if appropriate
178      if (LOG_IRQ)
179         logerror("%s: MSM6242 logging IRQ #%d\n", machine().describe_context(), (int) irq_type);
171180
172   // let's call the timer callback every tick
173   m_timer = timer_alloc(TIMER_RTC_CALLBACK);
174   m_timer->adjust(attotime::zero, 0, attotime::from_hz(clock()));
181      // ...and assert the output line
182      if (!m_res_out_int_func.isnull())
183         m_res_out_int_func(ASSERT_LINE);
184   }
185}
175186
176   // get real time from system
177   set_current_time(machine());
178187
179   // set up registers
180   m_tick = 0;
181   m_irq_flag = 0;
182   m_irq_type = 0;
183188
184   // TODO: skns writes 0x4 to D then expects E == 6 and F == 4, perhaps those are actually saved in the RTC CMOS?
185   m_reg[0] = 0;
186   m_reg[1] = 0x6;
187   m_reg[2] = 0x4;
189//-------------------------------------------------
190//  bump
191//-------------------------------------------------
188192
189   // save states
190   save_item(NAME(m_reg));
191   save_item(NAME(m_irq_flag));
192   save_item(NAME(m_irq_type));
193   save_item(NAME(m_tick));
193UINT64 msm6242_device::bump(int rtc_register, UINT64 delta, UINT64 register_min, UINT64 register_range)
194{
195   UINT64 carry = 0;
196
197   if (delta > 0)
198   {
199      // get the register value
200      UINT64 register_value = (rtc_register == RTC_TICKS)
201         ? m_tick
202         : get_clock_register(rtc_register);
203
204      // increment the value
205      UINT64 new_register_value = ((register_value - register_min + delta) % register_range) + register_min;
206
207      // calculate the cary
208      carry = ((register_value - register_min) + delta) / register_range;
209
210      // store the new register value
211      if (rtc_register == RTC_TICKS)
212         m_tick = (UINT16) new_register_value;
213      else
214         set_clock_register(rtc_register, (int) new_register_value);
215   }
216
217   return carry;
194218}
195219
196220
197221
198222//-------------------------------------------------
199//  device_reset - device-specific reset
223//  current_time
200224//-------------------------------------------------
201225
202void msm6242_device::device_reset()
226UINT64 msm6242_device::current_time()
203227{
204   if ( !m_res_out_int_func.isnull() )
205      m_res_out_int_func( CLEAR_LINE );
228   return machine().time().as_ticks(clock());
206229}
207230
208231
209232
233//-------------------------------------------------
234//  update_rtc_registers
235//-------------------------------------------------
236
237void msm6242_device::update_rtc_registers()
238{
239   // get the absolute current time, in ticks
240   UINT64 curtime = current_time();
241
242   // how long as it been since we last updated?
243   UINT64 delta = curtime - m_last_update_time;
244
245   // set current time
246   m_last_update_time = curtime;
247
248   // no delta?  just return
249   if (delta <= 0)
250      return;
251
252   // ticks
253   if ((m_tick % 200) != ((delta + m_tick) % 0x200))
254      irq(IRQ_64THSECOND);
255   delta = bump(RTC_TICKS, delta, 0, 0x8000);
256   if (delta <= 0)
257      return;
258
259   // seconds
260   irq(IRQ_SECOND);
261   delta = bump(RTC_SECOND, delta, 0, 60);
262   if (delta <= 0)
263      return;
264
265   // minutes
266   irq(IRQ_MINUTE);
267   delta = bump(RTC_MINUTE, delta, 0, 60);
268   if (delta <= 0)
269      return;
270
271   // hours
272   irq(IRQ_HOUR);
273   delta = bump(RTC_HOUR, delta, 0, 24);
274   if (delta <= 0)
275      return;
276
277   // days
278   while(delta--)
279      advance_days();
280}
281
282
283
284//-------------------------------------------------
285//  update_timer
286//-------------------------------------------------
287
288void msm6242_device::update_timer()
289{
290   UINT64 callback_ticks = 0;
291   attotime callback_time = attotime::never;
292
293   // we only need to call back if the IRQ flag is on, and we have a handler
294   if (!m_res_out_int_func.isnull() && m_irq_flag == 1)
295   {
296      switch(m_irq_type)
297      {     
298         case IRQ_HOUR:
299            callback_ticks += (59 - get_clock_register(RTC_MINUTE)) * (0x8000 * 60);
300            // fall through
301
302         case IRQ_MINUTE:
303            callback_ticks += (59 - get_clock_register(RTC_SECOND)) * 0x8000;
304            // fall through
305
306         case IRQ_SECOND:
307            callback_ticks += 0x8000 - m_tick;
308            break;
309
310         case IRQ_64THSECOND:
311            callback_ticks += 0x200 - (m_tick % 0x200);
312            break;
313      }
314   }
315
316   // if set, convert ticks to an attotime
317   if (callback_ticks > 0)
318   {
319      callback_time = attotime::from_ticks(callback_ticks, clock()) - machine().time();
320   }
321
322   m_timer->adjust(callback_time);
323}
324
325
326
327//-------------------------------------------------
328//  rtc_clock_updated
329//-------------------------------------------------
330
331void msm6242_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second)
332{
333   m_last_update_time = current_time();
334}
335
336
337
338//-------------------------------------------------
339//  rtc_timer_callback
340//-------------------------------------------------
341
342void msm6242_device::rtc_timer_callback()
343{
344   update_rtc_registers();
345}
346
347
348
349//-------------------------------------------------
350//  get_clock_nibble
351//-------------------------------------------------
352
353UINT8 msm6242_device::get_clock_nibble(int rtc_register, bool high)
354{
355   int value = get_clock_register(rtc_register);
356   value /= high ? 10 : 1;
357   return (UINT8) ((value % 10) & 0x0F);
358}
359
360
361
210362//**************************************************************************
211363//  READ/WRITE HANDLERS
212364//**************************************************************************
r19573r19574
217369
218370READ8_MEMBER( msm6242_device::read )
219371{
220   int sec = get_clock_register(RTC_SECOND);
221   int minute = get_clock_register(RTC_MINUTE);
222   int hour = get_clock_register(RTC_HOUR);
223   int day = get_clock_register(RTC_DAY);
224   int month = get_clock_register(RTC_MONTH);
225   int weekday = get_clock_register(RTC_DAY_OF_WEEK);
226   int year = get_clock_register(RTC_YEAR);
372   int hour, pm;
373   UINT8 result;
227374
375   // update the registers; they may have changed
376   update_rtc_registers();
377
228378   switch(offset)
229379   {
230      case MSM6242_REG_S1: return (sec % 10) & 0xf;
231      case MSM6242_REG_S10: return (sec / 10) & 0xf;
232      case MSM6242_REG_MI1: return (minute % 10) & 0xf;
233      case MSM6242_REG_MI10: return (minute / 10) & 0xf;
380      case MSM6242_REG_S1:
381         result = get_clock_nibble(RTC_SECOND, false);
382         break;
383
384      case MSM6242_REG_S10:
385         result = get_clock_nibble(RTC_SECOND, true);
386         break;
387
388      case MSM6242_REG_MI1:
389         result = get_clock_nibble(RTC_MINUTE, false);
390         break;
391
392      case MSM6242_REG_MI10:
393         result = get_clock_nibble(RTC_MINUTE, true);
394         break;
395
234396      case MSM6242_REG_H1:
235397      case MSM6242_REG_H10:
236      {
237         int pm = 0;
398         pm = 0;
399         hour = get_clock_register(RTC_HOUR);
238400
239         /* check for 12/24 hour mode */
240         if ((m_reg[2] & 0x04) == 0) /* 12 hour mode? */
401         // check for 12/24 hour mode
402         if ((m_reg[2] & 0x04) == 0) // 12 hour mode?
241403         {
242404            if (hour >= 12)
243405               pm = 1;
r19573r19574
249411         }
250412
251413         if ( offset == MSM6242_REG_H1 )
252            return hour % 10;
414            result = hour % 10;
415         else
416            result = (hour / 10) | (pm <<2);
417         break;
253418
254         return (hour / 10) | (pm <<2);
255      }
419      case MSM6242_REG_D1:
420         result = get_clock_nibble(RTC_DAY, false);
421         break;
256422
257      case MSM6242_REG_D1: return (day % 10) & 0xf;
258      case MSM6242_REG_D10: return (day / 10) & 0xf;
259      case MSM6242_REG_MO1: return (month % 10) & 0xf;
260      case MSM6242_REG_MO10: return (month / 10) & 0xf;
261      case MSM6242_REG_Y1: return (year % 10) & 0xf;
262      case MSM6242_REG_Y10: return ((year / 10) % 10) & 0xf;
263      case MSM6242_REG_W: return weekday;
264      case MSM6242_REG_CD: return m_reg[0];
265      case MSM6242_REG_CE: return m_reg[1];
266      case MSM6242_REG_CF: return m_reg[2];
423      case MSM6242_REG_D10:
424         result = get_clock_nibble(RTC_DAY, true);
425         break;
426
427      case MSM6242_REG_MO1:
428         result = get_clock_nibble(RTC_MONTH, false);
429         break;
430
431      case MSM6242_REG_MO10:
432         result = get_clock_nibble(RTC_MONTH, true);
433         break;
434
435      case MSM6242_REG_Y1:
436         result = get_clock_nibble(RTC_YEAR, false);
437         break;
438
439      case MSM6242_REG_Y10:
440         result = get_clock_nibble(RTC_YEAR, true);
441         break;
442
443      case MSM6242_REG_W:
444         result = (UINT8) (get_clock_register(RTC_DAY_OF_WEEK) - 1);
445         break;
446
447      case MSM6242_REG_CD:
448      case MSM6242_REG_CE:
449      case MSM6242_REG_CF:
450         result = m_reg[offset - MSM6242_REG_CD];
451         break;
452
453      default:
454         result = 0x00;
455         if (LOG_UNMAPPED)
456            logerror("%s: MSM6242 unmapped offset %02x read\n", machine().describe_context(), offset);
457         break;
267458   }
268459
269   logerror("%s: MSM6242 unmapped offset %02x read\n", machine().describe_context(), offset);
270   return 0;
460   return result;
271461}
272462
273463
r19573r19574
281471   switch(offset)
282472   {
283473      case MSM6242_REG_CD:
284      {
285474            //   x--- 30s ADJ
286475            //   -x-- IRQ FLAG
287476            //   --x- BUSY
288477            //   ---x HOLD
289
290478         m_reg[0] = data & 0x0f;
479         break;
291480
292         return;
293      }
294
295481      case MSM6242_REG_CE:
296      {
297482            //   xx-- t0,t1 (timing irq)
298483            //   --x- STD
299484            //   ---x MASK
300
301485         m_reg[1] = data & 0x0f;
302486         if((data & 3) == 0) // MASK & STD = 0
303487         {
r19573r19574
310494            if ( !m_res_out_int_func.isnull() )
311495               m_res_out_int_func( CLEAR_LINE );
312496         }
497         break;
313498
314         return;
315      }
316
317499      case MSM6242_REG_CF:
318      {
319500            //   x--- TEST
320501            //   -x-- 24/12
321502            //   --x- STOP
r19573r19574
326507            m_reg[2] = (m_reg[2] & ~0x04) | (data & 0x04);
327508         else
328509            m_reg[2] = (data & 0x0b) | (m_reg[2] & 4);
510         break;
329511
330         return;
331      }
512      default:
513         if (LOG_UNMAPPED)
514            logerror("%s: MSM6242 unmapped offset %02x written with %02x\n", machine().describe_context(), offset, data);
515         break;
332516   }
333517
334   logerror("%s: MSM6242 unmapped offset %02x written with %02x\n", machine().describe_context(), offset, data);
518   // update the timer variable in response to potential changes
519   update_timer();
335520}
trunk/src/emu/machine/msm6242.h
r19573r19574
4747   // device-level overrides
4848   virtual void device_start();
4949   virtual void device_reset();
50   virtual void device_pre_save();
51   virtual void device_post_load();
5052   virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
5153
54   // rtc overrides
55   virtual void rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second);
56
5257private:
58   static const int RTC_TICKS = ~0;
59
60   static const UINT8 IRQ_64THSECOND = 0;
61   static const UINT8 IRQ_SECOND = 1;
62   static const UINT8 IRQ_MINUTE = 2;
63   static const UINT8 IRQ_HOUR = 3;
64
5365   // state
5466   UINT8                  m_reg[3];
5567   UINT8                  m_irq_flag;
r19573r19574
5971   // incidentals
6072   devcb_resolved_write_line   m_res_out_int_func;
6173   emu_timer *               m_timer;
74   UINT64                  m_last_update_time;   // last update time, in clock cycles
6275
63   // callbacks
76   // methods
6477   void rtc_timer_callback();
78   UINT64 current_time();
79   void irq(UINT8 irq_type);
80   UINT64 bump(int rtc_register, UINT64 delta, UINT64 register_min, UINT64 register_range);
81   void update_rtc_registers();
82   void update_timer();
83   UINT8 get_clock_nibble(int rtc_register, bool high);
6584};
6685
6786
trunk/src/emu/dirtc.c
r19573r19574
157157
158158
159159//-------------------------------------------------
160//  advance_clock -
160//  advance_minutes
161161//-------------------------------------------------
162162
163163void device_rtc_interface::advance_minutes()
r19573r19574
173173   if (m_register[RTC_HOUR] == 24)
174174   {
175175      m_register[RTC_HOUR] = 0;
176      m_register[RTC_DAY]++;
177      m_register[RTC_DAY_OF_WEEK]++;
176      advance_days();
178177   }
178}
179179
180
181//-------------------------------------------------
182//  advance_days
183//-------------------------------------------------
184
185void device_rtc_interface::advance_days()
186{
187   m_register[RTC_DAY]++;
188   m_register[RTC_DAY_OF_WEEK]++;
189
180190   if (m_register[RTC_DAY_OF_WEEK] == 8)
181191   {
182192      m_register[RTC_DAY_OF_WEEK] = 1;
trunk/src/emu/dirtc.h
r19573r19574
6262
6363   void advance_seconds();
6464   void advance_minutes();
65   void advance_days();
6566   void adjust_seconds();
6667
6768   // derived class overrides

Previous 199869 Revisions Next


© 1997-2024 The MAME Team