1 /***************************************************************************
2
3 timer.c
4
5 Functions needed to generate timing and synchronization between several
6 CPUs.
7
8 Changes 2/27/99:
9 - added some rounding to the sorting of timers so that two timers
10 allocated to go off at the same time will go off in the order
11 they were allocated, without concern for floating point rounding
12 errors (thanks Juergen!)
13 - fixed a bug where the base_time was not updated when a CPU was
14 suspended, making subsequent calls to getabsolutetime() return an
15 incorrect time (thanks Nicola!)
16 - changed suspended CPUs so that they don't eat their timeslice until
17 all other CPUs have used up theirs; this allows a slave CPU to
18 trigger a higher priority CPU in the middle of the timeslice
19 - added the ability to call timer_reset() on a oneshot or pulse timer
20 from within that timer's callback; in this case, the timer won't
21 get removed (oneshot) or won't get reprimed (pulse)
22
23 Changes 12/17/99 (HJB):
24 - added overclocking factor and functions to set/get it at runtime.
25
26 Changes 12/23/99 (HJB):
27 - added burn() function pointer to tell CPU cores when we want to
28 burn cycles, because the cores might need to adjust internal
29 counters or timers.
30
31 ***************************************************************************/
32
33 #include "cpuintrf.h"
34 #include "driver.h"
35 #include "timer.h"
36
37 #define MAX_TIMERS 256
38
39 /*
40 * internal timer structures
41 */
42
43 typedef struct
44 {
45 int *icount;
46 void (*burn)(int cycles);
47 int index;
48 int suspended;
49 int trigger;
50 int nocount;
51 int lost;
52 timer_tm time;
53 timer_tm sec_to_cycles;
54 timer_tm cycles_to_sec;
55 float overclock;
56 } cpu_entry;
57
58
59 /* conversion constants */
60 timer_tm cycles_to_sec[MAX_CPU];
61 timer_tm sec_to_cycles[MAX_CPU];
62
63 /* list of per-CPU timer data */
64 static cpu_entry cpudata[MAX_CPU+1];
65 static cpu_entry *lastcpu;
66 static cpu_entry *activecpu;
67 static cpu_entry *last_activecpu;
68
69 /* list of active timers */
70 static timer_entry timers[MAX_TIMERS];
71 static timer_entry *timer_head;
72 static timer_entry *timer_free_head;
73
74 /* other internal states */
75 static timer_tm base_time;
76 static timer_tm global_offset;
77 static timer_entry *callback_timer;
78 static int callback_timer_modified;
79
80 /* prototypes */
81 static int pick_cpu(int *cpu, int *cycles, timer_tm expire);
82
83 /*
84 * return the current absolute time
85 */
getabsolutetime(void)86 timer_tm getabsolutetime(void)
87 {
88 if (activecpu && (*activecpu->icount + activecpu->lost) > 0)
89 return base_time - ((timer_tm)(*activecpu->icount + activecpu->lost) * activecpu->cycles_to_sec);
90 else
91 return base_time;
92 }
93
94
95 /*
96 * adjust the current CPU's timer so that a new event will fire at the right time
97 */
timer_adjust(timer_entry * timer,timer_tm time,timer_tm period)98 static INLINE void timer_adjust(timer_entry *timer, timer_tm time, timer_tm period)
99 {
100 int newicount, diff;
101
102 /* compute a new icount for the current CPU */
103 if (period == TIME_NOW)
104 newicount = 0;
105 else
106 newicount = CYCLES_CALC(timer->expire - time,activecpu->sec_to_cycles)+1;
107
108 /* determine if we're scheduled to run more cycles */
109 diff = *activecpu->icount - newicount;
110
111 /* if so, set the new icount and compute the amount of "lost" time */
112 if (diff > 0)
113 {
114 activecpu->lost += diff;
115 if (activecpu->burn)
116 (*activecpu->burn)(diff); /* let the CPU burn the cycles */
117 else
118 *activecpu->icount = newicount; /* CPU doesn't care */
119 }
120 }
121
122
123 /*
124 * allocate a new timer
125 */
timer_new(void)126 static INLINE timer_entry *timer_new(void)
127 {
128 timer_entry *timer;
129
130 /* remove an empty entry */
131 if (!timer_free_head)
132 return NULL;
133 timer = timer_free_head;
134 timer_free_head = timer->next;
135
136 return timer;
137 }
138
139
140 /*
141 * insert a new timer into the list at the appropriate location
142 */
timer_list_insert(timer_entry * timer)143 static INLINE void timer_list_insert(timer_entry *timer)
144 {
145 timer_entry *t=timer_head;
146 timer_entry *lt = NULL;
147 timer_tm expire = (t && timer->enabled) ? timer->expire : TIME_NEVER;
148
149 if (expire==TIME_NOW)
150 {
151 /* loop over the timer list */
152 for (t = timer_head; t; lt = t, t = t->next)
153 {
154 if (t->expire!=TIME_NOW)
155 {
156 /* link the new guy in before the current list entry */
157 timer->prev = t->prev;
158 timer->next = t;
159
160 if (t->prev)
161 t->prev->next = timer;
162 else
163 timer_head = timer;
164 t->prev = timer;
165 return;
166 }
167 }
168 }
169 else if (expire!=TIME_NEVER)
170 {
171 /* loop over the timer list */
172 for (t = timer_head; t; lt = t, t = t->next)
173 {
174 /* if the current list entry expires after us, we should be inserted before it */
175 /* note that due to floating point rounding, we need to allow a bit of slop here */
176 /* because two equal entries -- within rounding precision -- need to sort in */
177 /* the order they were inserted into the list */
178 //if ((t->expire - expire) > TIME_IN_NSEC(1))
179 if (t->expire > expire)
180 {
181 /* link the new guy in before the current list entry */
182 timer->prev = t->prev;
183 timer->next = t;
184
185 if (t->prev)
186 t->prev->next = timer;
187 else
188 timer_head = timer;
189 t->prev = timer;
190 return;
191 }
192 }
193 }
194 else
195 {
196 /* loop over the timer list */
197 for (t = timer_head; t; lt = t, t = t->next)
198 {
199 }
200 }
201
202 /* need to insert after the last one */
203 if (lt)
204 lt->next = timer;
205 else
206 timer_head = timer;
207 timer->prev = lt;
208 timer->next = NULL;
209 }
210
211
212 /*
213 * remove a timer from the linked list
214 */
timer_list_remove(timer_entry * timer)215 static INLINE void timer_list_remove(timer_entry *timer)
216 {
217 /* remove it from the list */
218 if (timer->prev)
219 timer->prev->next = timer->next;
220 else
221 timer_head = timer->next;
222 if (timer->next)
223 timer->next->prev = timer->prev;
224 }
225
226
227 /*
228 * initialize the timer system
229 */
timer_init(void)230 void timer_init(void)
231 {
232 cpu_entry *cpu;
233 int i;
234
235 /* keep a local copy of how many total CPU's */
236 lastcpu = cpudata + cpu_gettotalcpu() - 1;
237
238 /* we need to wait until the first call to timer_cyclestorun before using real CPU times */
239 base_time = 0;
240 global_offset = 0;
241 callback_timer = NULL;
242 callback_timer_modified = 0;
243
244 /* reset the timers */
245 memset(timers, 0, sizeof(timers));
246
247 /* initialize the lists */
248 timer_head = NULL;
249 timer_free_head = &timers[0];
250 for (i = 0; i < MAX_TIMERS-1; i++)
251 timers[i].next = &timers[i+1];
252
253 /* reset the CPU timers */
254 memset(cpudata, 0, sizeof(cpudata));
255 activecpu = NULL;
256 last_activecpu = lastcpu;
257
258 /* compute the cycle times */
259 for (cpu = cpudata, i = 0; cpu <= lastcpu; cpu++, i++)
260 {
261 /* make a pointer to this CPU's interface functions */
262 cpu->icount = cpuintf[Machine->drv->cpu[i].cpu_type & ~CPU_FLAGS_MASK].icount;
263 cpu->burn = cpuintf[Machine->drv->cpu[i].cpu_type & ~CPU_FLAGS_MASK].burn;
264
265 /* get the CPU's overclocking factor */
266 cpu->overclock = cpuintf[Machine->drv->cpu[i].cpu_type & ~CPU_FLAGS_MASK].overclock;
267
268 /* everyone is active but suspended by the reset line until further notice */
269 cpu->suspended = SUSPEND_REASON_RESET;
270
271 /* set the CPU index */
272 cpu->index = i;
273
274 /* compute the cycle times */
275 #ifndef MAME_UNDERCLOCK
276 cpu->sec_to_cycles = sec_to_cycles[i] = cpu->overclock * Machine->drv->cpu[i].cpu_clock;
277 #else
278 /* Franxis 21/01/2007 */
279 {
280 extern int underclock_sound;
281 extern int underclock_cpu;
282 if (Machine->drv->cpu[i].cpu_type&CPU_AUDIO_CPU)
283 cpu->sec_to_cycles = sec_to_cycles[i] = ((float)(cpu->overclock * Machine->drv->cpu[i].cpu_clock))
284 * ((((float)100)-((float)underclock_sound))/((float)100));
285 else
286 cpu->sec_to_cycles = sec_to_cycles[i] = ((float)(cpu->overclock * Machine->drv->cpu[i].cpu_clock))
287 * ((((float)100)-((float)underclock_cpu))/((float)100));
288 }
289 #endif
290 cpu->cycles_to_sec = cycles_to_sec[i] = TIME_ONE_SEC / sec_to_cycles[i];
291 }
292 }
293
294 /*
295 * get overclocking factor for a CPU
296 */
timer_get_overclock(int cpunum)297 float timer_get_overclock(int cpunum)
298 {
299 cpu_entry *cpu = &cpudata[cpunum];
300 return cpu->overclock;
301 }
302
303 /*
304 * set overclocking factor for a CPU
305 */
timer_set_overclock(int cpunum,float overclock)306 void timer_set_overclock(int cpunum, float overclock)
307 {
308 cpu_entry *cpu = &cpudata[cpunum];
309 cpu->overclock = overclock;
310 cpu->sec_to_cycles = sec_to_cycles[cpunum] = cpu->overclock * Machine->drv->cpu[cpunum].cpu_clock;
311 cpu->cycles_to_sec = cycles_to_sec[cpunum] = TIME_ONE_SEC / sec_to_cycles[cpunum];
312 }
313
314 /*
315 * allocate a pulse timer, which repeatedly calls the callback using the given period
316 */
timer_pulse(timer_tm period,int param,void (* callback)(int))317 void *timer_pulse(timer_tm period, int param, void (*callback)(int))
318 {
319 timer_tm time = getabsolutetime();
320 timer_entry *timer;
321
322 /* allocate a new entry */
323 timer = timer_new();
324 if (!timer)
325 return NULL;
326
327 /* fill in the record */
328 timer->callback = callback;
329 timer->callback_param = param;
330 timer->enabled = 1;
331 timer->period = period;
332
333 /* compute the time of the next firing and insert into the list */
334 timer->start = time;
335 if (period==TIME_NEVER)
336 timer->expire = TIME_NEVER;
337 else
338 timer->expire = time + period;
339
340 timer_list_insert(timer);
341
342 /* if we're supposed to fire before the end of this cycle, adjust the counter */
343 if (activecpu && timer->expire < base_time)
344 timer_adjust(timer, time, period);
345
346 /* return a handle */
347 return timer;
348 }
349
350
351 /*
352 * allocate a one-shot timer, which calls the callback after the given duration
353 */
timer_set(timer_tm duration,int param,void (* callback)(int))354 void *timer_set(timer_tm duration, int param, void (*callback)(int))
355 {
356 timer_tm time = getabsolutetime();
357 timer_entry *timer;
358
359 /* allocate a new entry */
360 timer = timer_new();
361 if (!timer)
362 return NULL;
363
364 /* fill in the record */
365 timer->callback = callback;
366 timer->callback_param = param;
367 timer->enabled = 1;
368 timer->period = 0;
369
370 /* compute the time of the next firing and insert into the list */
371 timer->start = time;
372 if (duration==TIME_NOW) timer->expire = time;
373 else if (duration==TIME_NEVER) timer->expire = TIME_NEVER;
374 else timer->expire = time + duration;
375 timer_list_insert(timer);
376
377 /* if we're supposed to fire before the end of this cycle, adjust the counter */
378 if (activecpu && timer->expire < base_time)
379 timer_adjust(timer, time, duration);
380
381 /* return a handle */
382 return timer;
383 }
384
385
386 /*
387 * reset the timing on a timer
388 */
timer_reset(void * which,timer_tm duration)389 void timer_reset(void *which, timer_tm duration)
390 {
391 timer_tm time = getabsolutetime();
392 timer_entry *timer = (timer_entry *)which;
393
394 /* compute the time of the next firing */
395 timer->start = time;
396 if (duration==TIME_NOW) timer->expire = time;
397 else if (duration==TIME_NEVER) timer->expire = TIME_NEVER;
398 else timer->expire = time + duration;
399
400 /* remove the timer and insert back into the list */
401 timer_list_remove(timer);
402 timer_list_insert(timer);
403
404 /* if we're supposed to fire before the end of this cycle, adjust the counter */
405 if (activecpu && timer->expire < base_time)
406 timer_adjust(timer, time, duration);
407
408 /* if this is the callback timer, mark it modified */
409 if (timer == callback_timer)
410 callback_timer_modified = 1;
411 }
412
413
414 /*
415 * remove a timer from the system
416 */
timer_remove(void * which)417 void timer_remove(void *which)
418 {
419 timer_entry *timer = (timer_entry *)which;
420
421 /* remove it from the list */
422 timer_list_remove(timer);
423
424 /* free it up by adding it back to the free list */
425 timer->next = timer_free_head;
426 timer_free_head = timer;
427 }
428
429
430 /*
431 * enable/disable a timer
432 */
timer_enable(void * which,int enable)433 int timer_enable(void *which, int enable)
434 {
435 timer_entry *timer = (timer_entry *)which;
436 int old;
437
438 /* set the enable flag */
439 old = timer->enabled;
440 timer->enabled = enable;
441
442 /* remove the timer and insert back into the list */
443 timer_list_remove(timer);
444 timer_list_insert(timer);
445
446 return old;
447 }
448
449
450 /*
451 * return the time since the last trigger
452 */
timer_timeelapsed(void * which)453 timer_tm timer_timeelapsed(void *which)
454 {
455 timer_tm time = getabsolutetime();
456 timer_entry *timer = (timer_entry *)which;
457
458 return time - timer->start;
459 }
460
461
462 /*
463 * return the time until the next trigger
464 */
timer_timeleft(void * which)465 timer_tm timer_timeleft(void *which)
466 {
467 timer_tm time = getabsolutetime();
468 timer_entry *timer = (timer_entry *)which;
469
470 if (timer->expire==TIME_NEVER)
471 return TIME_NEVER;
472 else
473 return timer->expire - time;
474 }
475
476
477 /*
478 * return the current time
479 */
timer_get_time(void)480 float timer_get_time(void)
481 {
482 return (float)global_offset + ((float)getabsolutetime()/(float)TIME_ONE_SEC);
483 }
484
485
486 /*
487 * begin CPU execution by determining how many cycles the CPU should run
488 */
timer_schedule_cpu(int * cpu,int * cycles)489 int timer_schedule_cpu(int *cpu, int *cycles)
490 {
491 timer_tm end;
492
493 /* then see if there are any CPUs that aren't suspended and haven't yet been updated */
494 if (pick_cpu(cpu, cycles, timer_head->expire))
495 return 1;
496
497 /* everyone is up-to-date; expire any timers now */
498 end = timer_head->expire;
499 while (timer_head->expire <= end)
500 {
501 timer_entry *timer = timer_head;
502
503 /* the base time is now the time of the timer */
504 base_time = timer->expire;
505
506 /* set the global state of which callback we're in */
507 callback_timer_modified = 0;
508 callback_timer = timer;
509
510 /* call the callback */
511 if (timer->callback)
512 {
513 profiler_mark(PROFILER_TIMER_CALLBACK);
514 (*timer->callback)(timer->callback_param);
515 profiler_mark(PROFILER_END);
516 }
517
518 /* clear the callback timer global */
519 callback_timer = NULL;
520
521 /* reset or remove the timer, but only if it wasn't modified during the callback */
522 if (!callback_timer_modified)
523 {
524 if (timer->period)
525 {
526 timer->start = timer->expire;
527 timer->expire += timer->period;
528
529 timer_list_remove(timer);
530 timer_list_insert(timer);
531 }
532 else
533 timer_remove(timer);
534 }
535 }
536
537 /* reset scheduling so it starts with CPU 0 */
538 last_activecpu = lastcpu;
539
540 /* go back to scheduling */
541 return pick_cpu(cpu, cycles, timer_head->expire);
542 }
543
544
545 /*
546 * end CPU execution by updating the number of cycles the CPU actually ran
547 */
timer_update_cpu(int cpunum,int ran)548 void timer_update_cpu(int cpunum, int ran)
549 {
550 cpu_entry *cpu = cpudata + cpunum;
551
552 /* update the time if we haven't been suspended */
553 if (!cpu->suspended)
554 {
555 cpu->time += (timer_tm)(ran - cpu->lost) * cpu->cycles_to_sec;
556 cpu->lost = 0;
557 }
558
559 /* time to renormalize? */
560 if (cpu->time >= TIME_ONE_SEC)
561 {
562 timer_entry *timer;
563 cpu_entry *c;
564
565 /* renormalize all the CPU timers */
566 for (c = cpudata; c <= lastcpu; c++)
567 c->time -= TIME_ONE_SEC;
568
569 /* renormalize all the timers' times */
570 for (timer = timer_head; timer; timer = timer->next)
571 {
572 timer->start -= TIME_ONE_SEC;
573 if (timer->expire!=TIME_NEVER)
574 timer->expire -= TIME_ONE_SEC;
575 }
576
577 /* renormalize the global timers */
578 global_offset += 1;
579 }
580
581 /* now stop counting cycles */
582 base_time = cpu->time;
583 activecpu = NULL;
584 }
585
586
587 /*
588 * suspend a CPU but continue to count time for it
589 */
timer_suspendcpu(int cpunum,int suspend,int reason)590 void timer_suspendcpu(int cpunum, int suspend, int reason)
591 {
592 cpu_entry *cpu = cpudata + cpunum;
593 int nocount = cpu->nocount;
594 int old = cpu->suspended;
595
596 /* mark the CPU */
597 if (suspend)
598 cpu->suspended |= reason;
599 else
600 cpu->suspended &= ~reason;
601 cpu->nocount = 0;
602
603 /* if this is the active CPU and we're halting, stop immediately */
604 if (activecpu && cpu == activecpu && !old && cpu->suspended)
605 {
606 /* set the CPU's time to the current time */
607 cpu->time = base_time = getabsolutetime(); /* ASG 990225 - also set base_time */
608 cpu->lost = 0;
609
610 /* no more instructions */
611 if (cpu->burn)
612 (*cpu->burn)(*cpu->icount); /* let the CPU burn the cycles */
613 else
614 *cpu->icount = 0; /* CPU doesn't care */
615 }
616
617 /* else if we're unsuspending a CPU, reset its time */
618 else if (old && !cpu->suspended && !nocount)
619 {
620 timer_tm time = getabsolutetime();
621
622 /* only update the time if it's later than the CPU's time */
623 if (time > cpu->time)
624 cpu->time = time;
625 cpu->lost = 0;
626 }
627 }
628
629
630
631 /*
632 * hold a CPU and don't count time for it
633 */
timer_holdcpu(int cpunum,int hold,int reason)634 void timer_holdcpu(int cpunum, int hold, int reason)
635 {
636 cpu_entry *cpu = cpudata + cpunum;
637
638 /* same as suspend */
639 timer_suspendcpu(cpunum, hold, reason);
640
641 /* except that we don't count time */
642 if (hold)
643 cpu->nocount = 1;
644 }
645
646
647
648 /*
649 * query if a CPU is suspended or not
650 */
timer_iscpususpended(int cpunum,int reason)651 int timer_iscpususpended(int cpunum, int reason)
652 {
653 cpu_entry *cpu = cpudata + cpunum;
654 return (cpu->suspended & reason) && !cpu->nocount;
655 }
656
657
658
659 /*
660 * query if a CPU is held or not
661 */
timer_iscpuheld(int cpunum,int reason)662 int timer_iscpuheld(int cpunum, int reason)
663 {
664 cpu_entry *cpu = cpudata + cpunum;
665 return (cpu->suspended & reason) && cpu->nocount;
666 }
667
668
669
670 /*
671 * suspend a CPU until a specified trigger condition is met
672 */
timer_suspendcpu_trigger(int cpunum,int trigger)673 void timer_suspendcpu_trigger(int cpunum, int trigger)
674 {
675 cpu_entry *cpu = cpudata + cpunum;
676
677 /* suspend the CPU immediately if it's not already */
678 timer_suspendcpu(cpunum, 1, SUSPEND_REASON_TRIGGER);
679
680 /* set the trigger */
681 cpu->trigger = trigger;
682 }
683
684
685
686 /*
687 * hold a CPU and don't count time for it
688 */
timer_holdcpu_trigger(int cpunum,int trigger)689 void timer_holdcpu_trigger(int cpunum, int trigger)
690 {
691 cpu_entry *cpu = cpudata + cpunum;
692
693 /* suspend the CPU immediately if it's not already */
694 timer_holdcpu(cpunum, 1, SUSPEND_REASON_TRIGGER);
695
696 /* set the trigger */
697 cpu->trigger = trigger;
698 }
699
700
701
702 /*
703 * generates a trigger to unsuspend any CPUs waiting for it
704 */
timer_trigger(int trigger)705 void timer_trigger(int trigger)
706 {
707 cpu_entry *cpu;
708
709 /* cause an immediate resynchronization */
710 if (activecpu)
711 {
712 int left = *activecpu->icount;
713 if (left > 0)
714 {
715 activecpu->lost += left;
716 if (activecpu->burn)
717 (*activecpu->burn)(left); /* let the CPU burn the cycles */
718 else
719 *activecpu->icount = 0; /* CPU doesn't care */
720 }
721 }
722
723 /* look for suspended CPUs waiting for this trigger and unsuspend them */
724 for (cpu = cpudata; cpu <= lastcpu; cpu++)
725 {
726 if (cpu->suspended && cpu->trigger == trigger)
727 {
728 timer_suspendcpu(cpu->index, 0, SUSPEND_REASON_TRIGGER);
729 cpu->trigger = 0;
730 }
731 }
732 }
733
734
735 /*
736 * pick the next CPU to run
737 */
pick_cpu(int * cpunum,int * cycles,timer_tm end)738 static int pick_cpu(int *cpunum, int *cycles, timer_tm end)
739 {
740 cpu_entry *cpu = last_activecpu;
741
742 /* look for a CPU that isn't suspended and hasn't run its full timeslice yet */
743 do
744 {
745 /* wrap around */
746 cpu += 1;
747 if (cpu > lastcpu)
748 cpu = cpudata;
749
750 /* if this CPU isn't suspended and has time left.... */
751 if ((!cpu->suspended) && (cpu->time < end))
752 {
753 /* mark the CPU active, and remember the CPU number locally */
754 activecpu = last_activecpu = cpu;
755
756 /* return the number of cycles to execute and the CPU number */
757 *cpunum = cpu->index;
758 *cycles=CYCLES_CALC(end - cpu->time,cpu->sec_to_cycles);
759
760 if (*cycles > 0)
761 {
762 /* remember the base time for this CPU */
763 base_time = cpu->time + ((timer_tm)*cycles * cpu->cycles_to_sec);
764
765 /* success */
766 return 1;
767 }
768 }
769 }
770 while (cpu != last_activecpu);
771
772 /* ASG 990225 - bump all suspended CPU times after the slice has finished */
773 for (cpu = cpudata; cpu <= lastcpu; cpu++)
774 if (cpu->suspended && !cpu->nocount)
775 {
776 cpu->time = end;
777 cpu->lost = 0;
778 }
779
780 /* failure */
781 return 0;
782 }
783