1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4
5 schedule.cpp
6
7 Core device execution and scheduling engine.
8
9 ***************************************************************************/
10
11 #include "emu.h"
12 #include "debugger.h"
13
14 //**************************************************************************
15 // DEBUGGING
16 //**************************************************************************
17
18 #define VERBOSE 0
19
20 #define LOG(...) do { if (VERBOSE) machine().logerror(__VA_ARGS__); } while (0)
21 #define PRECISION
22
23
24
25 //**************************************************************************
26 // CONSTANTS
27 //**************************************************************************
28
29 // internal trigger IDs
30 enum
31 {
32 TRIGGER_INT = -2000,
33 TRIGGER_YIELDTIME = -3000,
34 TRIGGER_SUSPENDTIME = -4000
35 };
36
37
38
39 //**************************************************************************
40 // EMU TIMER
41 //**************************************************************************
42
43 //-------------------------------------------------
44 // emu_timer - constructor
45 //-------------------------------------------------
46
emu_timer()47 emu_timer::emu_timer() :
48 m_machine(nullptr),
49 m_next(nullptr),
50 m_prev(nullptr),
51 m_param(0),
52 m_ptr(nullptr),
53 m_enabled(false),
54 m_temporary(false),
55 m_period(attotime::zero),
56 m_start(attotime::zero),
57 m_expire(attotime::never),
58 m_device(nullptr),
59 m_id(0)
60 {
61 }
62
63
64 //-------------------------------------------------
65 // ~emu_timer - destructor
66 //-------------------------------------------------
67
~emu_timer()68 emu_timer::~emu_timer()
69 {
70 }
71
72
73 //-------------------------------------------------
74 // init - completely initialize the state when
75 // re-allocated as a non-device timer
76 //-------------------------------------------------
77
init(running_machine & machine,timer_expired_delegate callback,void * ptr,bool temporary)78 inline emu_timer &emu_timer::init(running_machine &machine, timer_expired_delegate callback, void *ptr, bool temporary)
79 {
80 // ensure the entire timer state is clean
81 m_machine = &machine;
82 m_next = nullptr;
83 m_prev = nullptr;
84 m_callback = callback;
85 m_param = 0;
86 m_ptr = ptr;
87 m_enabled = false;
88 m_temporary = temporary;
89 m_period = attotime::never;
90 m_start = machine.time();
91 m_expire = attotime::never;
92 m_device = nullptr;
93 m_id = 0;
94
95 // if we're not temporary, register ourselves with the save state system
96 if (!m_temporary)
97 register_save();
98
99 // insert into the list
100 machine.scheduler().timer_list_insert(*this);
101 return *this;
102 }
103
104
105 //-------------------------------------------------
106 // init - completely initialize the state when
107 // re-allocated as a device timer
108 //-------------------------------------------------
109
init(device_t & device,device_timer_id id,void * ptr,bool temporary)110 inline emu_timer &emu_timer::init(device_t &device, device_timer_id id, void *ptr, bool temporary)
111 {
112 // ensure the entire timer state is clean
113 m_machine = &device.machine();
114 m_next = nullptr;
115 m_prev = nullptr;
116 m_callback = timer_expired_delegate(FUNC(emu_timer::device_timer_expired), this);
117 m_param = 0;
118 m_ptr = ptr;
119 m_enabled = false;
120 m_temporary = temporary;
121 m_period = attotime::never;
122 m_start = machine().time();
123 m_expire = attotime::never;
124 m_device = &device;
125 m_id = id;
126
127 // if we're not temporary, register ourselves with the save state system
128 if (!m_temporary)
129 register_save();
130
131 // insert into the list
132 machine().scheduler().timer_list_insert(*this);
133 return *this;
134 }
135
136
137 //-------------------------------------------------
138 // release - release us from the global list
139 // management when deallocating
140 //-------------------------------------------------
141
release()142 inline emu_timer &emu_timer::release()
143 {
144 // unhook us from the global list
145 machine().scheduler().timer_list_remove(*this);
146 return *this;
147 }
148
149
150 //-------------------------------------------------
151 // enable - enable/disable a timer
152 //-------------------------------------------------
153
enable(bool enable)154 bool emu_timer::enable(bool enable)
155 {
156 // reschedule only if the state has changed
157 const bool old = m_enabled;
158 if (old != enable)
159 {
160 // set the enable flag
161 m_enabled = enable;
162
163 // remove the timer and insert back into the list
164 machine().scheduler().timer_list_remove(*this);
165 machine().scheduler().timer_list_insert(*this);
166 }
167 return old;
168 }
169
170
171 //-------------------------------------------------
172 // adjust - adjust the time when this timer will
173 // fire and specify a period for subsequent
174 // firings
175 //-------------------------------------------------
176
adjust(attotime start_delay,s32 param,const attotime & period)177 void emu_timer::adjust(attotime start_delay, s32 param, const attotime &period)
178 {
179 // if this is the callback timer, mark it modified
180 device_scheduler &scheduler = machine().scheduler();
181 if (scheduler.m_callback_timer == this)
182 scheduler.m_callback_timer_modified = true;
183
184 // compute the time of the next firing and insert into the list
185 m_param = param;
186 m_enabled = true;
187
188 // clamp negative times to 0
189 if (start_delay.seconds() < 0)
190 start_delay = attotime::zero;
191
192 // set the start and expire times
193 m_start = scheduler.time();
194 m_expire = m_start + start_delay;
195 m_period = period;
196
197 // remove and re-insert the timer in its new order
198 scheduler.timer_list_remove(*this);
199 scheduler.timer_list_insert(*this);
200
201 // if this was inserted as the head, abort the current timeslice and resync
202 if (this == scheduler.first_timer())
203 scheduler.abort_timeslice();
204 }
205
206
207 //-------------------------------------------------
208 // elapsed - return the amount of time since the
209 // timer was started
210 //-------------------------------------------------
211
elapsed() const212 attotime emu_timer::elapsed() const noexcept
213 {
214 return machine().time() - m_start;
215 }
216
217
218 //-------------------------------------------------
219 // remaining - return the amount of time
220 // remaining until the timer expires
221 //-------------------------------------------------
222
remaining() const223 attotime emu_timer::remaining() const noexcept
224 {
225 attotime curtime = machine().time();
226 if (curtime >= m_expire)
227 return attotime::zero;
228 return m_expire - curtime;
229 }
230
231
232 //-------------------------------------------------
233 // register_save - register ourself with the save
234 // state system
235 //-------------------------------------------------
236
register_save()237 void emu_timer::register_save()
238 {
239 // determine our instance number and name
240 int index = 0;
241 std::string name;
242
243 if (m_device == nullptr)
244 {
245 // for non-device timers, it is an index based on the callback function name
246 name = m_callback.name() ? m_callback.name() : "unnamed";
247 for (emu_timer *curtimer = machine().scheduler().first_timer(); curtimer != nullptr; curtimer = curtimer->next())
248 if (!curtimer->m_temporary && curtimer->m_device == nullptr)
249 {
250 if (curtimer->m_callback.name() != nullptr && m_callback.name() != nullptr && strcmp(curtimer->m_callback.name(), m_callback.name()) == 0)
251 index++;
252 else if (curtimer->m_callback.name() == nullptr && m_callback.name() == nullptr)
253 index++;
254 }
255 }
256 else
257 {
258 // for device timers, it is an index based on the device and timer ID
259 name = string_format("%s/%d", m_device->tag(), m_id);
260 for (emu_timer *curtimer = machine().scheduler().first_timer(); curtimer != nullptr; curtimer = curtimer->next())
261 if (!curtimer->m_temporary && curtimer->m_device == m_device && curtimer->m_id == m_id)
262 index++;
263 }
264
265 // save the bits
266 machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_param));
267 machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_enabled));
268 machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_period));
269 machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_start));
270 machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_expire));
271 }
272
273
274 //-------------------------------------------------
275 // schedule_next_period - schedule the next
276 // period
277 //-------------------------------------------------
278
schedule_next_period()279 inline void emu_timer::schedule_next_period()
280 {
281 // advance by one period
282 m_start = m_expire;
283 m_expire += m_period;
284
285 // remove and re-insert us
286 device_scheduler &scheduler = machine().scheduler();
287 scheduler.timer_list_remove(*this);
288 scheduler.timer_list_insert(*this);
289 }
290
291
292 //-------------------------------------------------
293 // dump - dump internal state to a single output
294 // line in the error log
295 //-------------------------------------------------
296
dump() const297 void emu_timer::dump() const
298 {
299 machine().logerror("%p: en=%d temp=%d exp=%15s start=%15s per=%15s param=%d ptr=%p", this, m_enabled, m_temporary, m_expire.as_string(PRECISION), m_start.as_string(PRECISION), m_period.as_string(PRECISION), m_param, m_ptr);
300 if (m_device == nullptr)
301 if (m_callback.name() == nullptr)
302 machine().logerror(" cb=NULL\n");
303 else
304 machine().logerror(" cb=%s\n", m_callback.name());
305 else
306 machine().logerror(" dev=%s id=%d\n", m_device->tag(), m_id);
307 }
308
309
310 //-------------------------------------------------
311 // device_timer_expired - trampoline to avoid a
312 // conditional jump on the hot path
313 //-------------------------------------------------
314
device_timer_expired(emu_timer & timer,void * ptr,s32 param)315 void emu_timer::device_timer_expired(emu_timer &timer, void *ptr, s32 param)
316 {
317 timer.m_device->timer_expired(timer, timer.m_id, param, ptr);
318 }
319
320
321
322 //**************************************************************************
323 // DEVICE SCHEDULER
324 //**************************************************************************
325
326 //-------------------------------------------------
327 // device_scheduler - constructor
328 //-------------------------------------------------
329
device_scheduler(running_machine & machine)330 device_scheduler::device_scheduler(running_machine &machine) :
331 m_machine(machine),
332 m_executing_device(nullptr),
333 m_execute_list(nullptr),
334 m_basetime(attotime::zero),
335 m_timer_list(nullptr),
336 m_callback_timer(nullptr),
337 m_callback_timer_modified(false),
338 m_callback_timer_expire_time(attotime::zero),
339 m_suspend_changes_pending(true),
340 m_quantum_minimum(ATTOSECONDS_IN_NSEC(1) / 1000)
341 {
342 // append a single never-expiring timer so there is always one in the list
343 m_timer_list = &m_timer_allocator.alloc()->init(machine, timer_expired_delegate(), nullptr, true);
344 m_timer_list->adjust(attotime::never);
345
346 // register global states
347 machine.save().save_item(NAME(m_basetime));
348 machine.save().register_presave(save_prepost_delegate(FUNC(device_scheduler::presave), this));
349 machine.save().register_postload(save_prepost_delegate(FUNC(device_scheduler::postload), this));
350 }
351
352
353 //-------------------------------------------------
354 // device_scheduler - destructor
355 //-------------------------------------------------
356
~device_scheduler()357 device_scheduler::~device_scheduler()
358 {
359 // remove all timers
360 while (m_timer_list != nullptr)
361 m_timer_allocator.reclaim(m_timer_list->release());
362 }
363
364
365 //-------------------------------------------------
366 // time - return the current time
367 //-------------------------------------------------
368
time() const369 attotime device_scheduler::time() const noexcept
370 {
371 // if we're currently in a callback, use the timer's expiration time as a base
372 if (m_callback_timer != nullptr)
373 return m_callback_timer_expire_time;
374
375 // if we're executing as a particular CPU, use its local time as a base
376 // otherwise, return the global base time
377 return (m_executing_device != nullptr) ? m_executing_device->local_time() : m_basetime;
378 }
379
380
381 //-------------------------------------------------
382 // can_save - return true if it's safe to save
383 // (i.e., no temporary timers outstanding)
384 //-------------------------------------------------
385
can_save() const386 bool device_scheduler::can_save() const
387 {
388 // if any live temporary timers exit, fail
389 for (emu_timer *timer = m_timer_list; timer != nullptr; timer = timer->next())
390 if (timer->m_temporary && !timer->expire().is_never())
391 {
392 machine().logerror("Failed save state attempt due to anonymous timers:\n");
393 dump_timers();
394 return false;
395 }
396
397 // otherwise, we're good
398 return true;
399 }
400
401
402 //-------------------------------------------------
403 // apply_suspend_changes - applies suspend/resume
404 // changes to all device_execute_interfaces
405 //-------------------------------------------------
406
apply_suspend_changes()407 inline void device_scheduler::apply_suspend_changes()
408 {
409 u32 suspendchanged = 0;
410 for (device_execute_interface *exec = m_execute_list; exec != nullptr; exec = exec->m_nextexec)
411 {
412 suspendchanged |= exec->m_suspend ^ exec->m_nextsuspend;
413 exec->m_suspend = exec->m_nextsuspend;
414 exec->m_nextsuspend &= ~SUSPEND_REASON_TIMESLICE;
415 exec->m_eatcycles = exec->m_nexteatcycles;
416 }
417
418 // recompute the execute list if any CPUs changed their suspension state
419 if (suspendchanged != 0)
420 rebuild_execute_list();
421 else
422 m_suspend_changes_pending = false;
423 }
424
425
426 //-------------------------------------------------
427 // timeslice - execute all devices for a single
428 // timeslice
429 //-------------------------------------------------
430
timeslice()431 void device_scheduler::timeslice()
432 {
433 bool call_debugger = ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0);
434
435 // build the execution list if we don't have one yet
436 if (UNEXPECTED(m_execute_list == nullptr))
437 rebuild_execute_list();
438
439 // if the current quantum has expired, find a new one
440 while (m_basetime >= m_quantum_list.first()->m_expire)
441 m_quantum_allocator.reclaim(m_quantum_list.detach_head());
442
443 // loop until we hit the next timer
444 while (m_basetime < m_timer_list->m_expire)
445 {
446 // by default, assume our target is the end of the next quantum
447 attotime target(m_basetime + attotime(0, m_quantum_list.first()->m_actual));
448
449 // however, if the next timer is going to fire before then, override
450 if (m_timer_list->m_expire < target)
451 target = m_timer_list->m_expire;
452
453 LOG("------------------\n");
454 LOG("cpu_timeslice: target = %s\n", target.as_string(PRECISION));
455
456 // do we have pending suspension changes?
457 if (m_suspend_changes_pending)
458 apply_suspend_changes();
459
460 // loop over all CPUs
461 for (device_execute_interface *exec = m_execute_list; exec != nullptr; exec = exec->m_nextexec)
462 {
463 // only process if this CPU is executing or truly halted (not yielding)
464 // and if our target is later than the CPU's current time (coarse check)
465 if (EXPECTED((exec->m_suspend == 0 || exec->m_eatcycles) && target.seconds() >= exec->m_localtime.seconds()))
466 {
467 // compute how many attoseconds to execute this CPU
468 attoseconds_t delta = target.attoseconds() - exec->m_localtime.attoseconds();
469 if (delta < 0 && target.seconds() > exec->m_localtime.seconds())
470 delta += ATTOSECONDS_PER_SECOND;
471 assert(delta == (target - exec->m_localtime).as_attoseconds());
472
473 if (exec->m_attoseconds_per_cycle == 0)
474 {
475 exec->m_localtime = target;
476 }
477 // if we have enough for at least 1 cycle, do the math
478 else if (delta >= exec->m_attoseconds_per_cycle)
479 {
480 // compute how many cycles we want to execute
481 int ran = exec->m_cycles_running = divu_64x32(u64(delta) >> exec->m_divshift, exec->m_divisor);
482 LOG(" cpu '%s': %d (%d cycles)\n", exec->device().tag(), delta, exec->m_cycles_running);
483
484 // if we're not suspended, actually execute
485 if (exec->m_suspend == 0)
486 {
487 g_profiler.start(exec->m_profiler);
488
489 // note that this global variable cycles_stolen can be modified
490 // via the call to cpu_execute
491 exec->m_cycles_stolen = 0;
492 m_executing_device = exec;
493 *exec->m_icountptr = exec->m_cycles_running;
494 if (!call_debugger)
495 exec->run();
496 else
497 {
498 exec->debugger_start_cpu_hook(target);
499 exec->run();
500 exec->debugger_stop_cpu_hook();
501 }
502
503 // adjust for any cycles we took back
504 assert(ran >= *exec->m_icountptr);
505 ran -= *exec->m_icountptr;
506 assert(ran >= exec->m_cycles_stolen);
507 ran -= exec->m_cycles_stolen;
508 g_profiler.stop();
509 }
510
511 // account for these cycles
512 exec->m_totalcycles += ran;
513
514 // update the local time for this CPU
515 attotime deltatime;
516 if (ran < exec->m_cycles_per_second)
517 deltatime = attotime(0, exec->m_attoseconds_per_cycle * ran);
518 else
519 {
520 u32 remainder;
521 s32 secs = divu_64x32_rem(ran, exec->m_cycles_per_second, &remainder);
522 deltatime = attotime(secs, u64(remainder) * exec->m_attoseconds_per_cycle);
523 }
524 assert(deltatime >= attotime::zero);
525 exec->m_localtime += deltatime;
526 LOG(" %d ran, %d total, time = %s\n", ran, s32(exec->m_totalcycles), exec->m_localtime.as_string(PRECISION));
527
528 // if the new local CPU time is less than our target, move the target up, but not before the base
529 if (exec->m_localtime < target)
530 {
531 target = std::max(exec->m_localtime, m_basetime);
532 LOG(" (new target)\n");
533 }
534 }
535 }
536 }
537 m_executing_device = nullptr;
538
539 // update the base time
540 m_basetime = target;
541 }
542
543 // execute timers
544 execute_timers();
545 }
546
547
548 //-------------------------------------------------
549 // abort_timeslice - abort execution for the
550 // current timeslice
551 //-------------------------------------------------
552
abort_timeslice()553 void device_scheduler::abort_timeslice()
554 {
555 if (m_executing_device != nullptr)
556 m_executing_device->abort_timeslice();
557 }
558
559
560 //-------------------------------------------------
561 // trigger - generate a global trigger
562 //-------------------------------------------------
563
trigger(int trigid,const attotime & after)564 void device_scheduler::trigger(int trigid, const attotime &after)
565 {
566 // ensure we have a list of executing devices
567 if (m_execute_list == nullptr)
568 rebuild_execute_list();
569
570 // if we have a non-zero time, schedule a timer
571 if (after != attotime::zero)
572 timer_set(after, timer_expired_delegate(FUNC(device_scheduler::timed_trigger), this), trigid);
573
574 // send the trigger to everyone who cares
575 else
576 for (device_execute_interface *exec = m_execute_list; exec != nullptr; exec = exec->m_nextexec)
577 exec->trigger(trigid);
578 }
579
580
581 //-------------------------------------------------
582 // boost_interleave - temporarily boosts the
583 // interleave factor
584 //-------------------------------------------------
585
boost_interleave(const attotime & timeslice_time,const attotime & boost_duration)586 void device_scheduler::boost_interleave(const attotime ×lice_time, const attotime &boost_duration)
587 {
588 // ignore timeslices > 1 second
589 if (timeslice_time.seconds() > 0)
590 return;
591 add_scheduling_quantum(timeslice_time, boost_duration);
592 }
593
594
595 //-------------------------------------------------
596 // timer_alloc - allocate a global non-device
597 // timer and return a pointer
598 //-------------------------------------------------
599
timer_alloc(timer_expired_delegate callback,void * ptr)600 emu_timer *device_scheduler::timer_alloc(timer_expired_delegate callback, void *ptr)
601 {
602 return &m_timer_allocator.alloc()->init(machine(), callback, ptr, false);
603 }
604
605
606 //-------------------------------------------------
607 // timer_set - allocate an anonymous non-device
608 // timer and set it to go off after the given
609 // amount of time
610 //-------------------------------------------------
611
timer_set(const attotime & duration,timer_expired_delegate callback,int param,void * ptr)612 void device_scheduler::timer_set(const attotime &duration, timer_expired_delegate callback, int param, void *ptr)
613 {
614 m_timer_allocator.alloc()->init(machine(), callback, ptr, true).adjust(duration, param);
615 }
616
617
618 //-------------------------------------------------
619 // timer_alloc - allocate a global device timer
620 // and return a pointer
621 //-------------------------------------------------
622
timer_alloc(device_t & device,device_timer_id id,void * ptr)623 emu_timer *device_scheduler::timer_alloc(device_t &device, device_timer_id id, void *ptr)
624 {
625 return &m_timer_allocator.alloc()->init(device, id, ptr, false);
626 }
627
628
629 //-------------------------------------------------
630 // timer_set - allocate an anonymous device timer
631 // and set it to go off after the given amount of
632 // time
633 //-------------------------------------------------
634
timer_set(const attotime & duration,device_t & device,device_timer_id id,int param,void * ptr)635 void device_scheduler::timer_set(const attotime &duration, device_t &device, device_timer_id id, int param, void *ptr)
636 {
637 m_timer_allocator.alloc()->init(device, id, ptr, true).adjust(duration, param);
638 }
639
640
641 //-------------------------------------------------
642 // eat_all_cycles - eat a ton of cycles on all
643 // CPUs to force a quick exit
644 //-------------------------------------------------
645
eat_all_cycles()646 void device_scheduler::eat_all_cycles()
647 {
648 for (device_execute_interface *exec = m_execute_list; exec != nullptr; exec = exec->m_nextexec)
649 exec->eat_cycles(1000000000);
650 }
651
652
653 //-------------------------------------------------
654 // timed_trigger - generate a trigger after a
655 // given amount of time
656 //-------------------------------------------------
657
timed_trigger(void * ptr,s32 param)658 void device_scheduler::timed_trigger(void *ptr, s32 param)
659 {
660 trigger(param);
661 }
662
663
664 //-------------------------------------------------
665 // presave - before creating a save state
666 //-------------------------------------------------
667
presave()668 void device_scheduler::presave()
669 {
670 // report the timer state after a log
671 LOG("Prior to saving state:\n");
672 #if VERBOSE
673 dump_timers();
674 #endif
675 }
676
677
678 //-------------------------------------------------
679 // postload - after loading a save state
680 //-------------------------------------------------
681
postload()682 void device_scheduler::postload()
683 {
684 // remove all timers and make a private list of permanent ones
685 simple_list<emu_timer> private_list;
686 while (m_timer_list != nullptr)
687 {
688 emu_timer &timer = *m_timer_list;
689
690 // temporary timers go away entirely (except our special never-expiring one)
691 if (timer.m_temporary && !timer.expire().is_never())
692 m_timer_allocator.reclaim(timer.release());
693
694 // permanent ones get added to our private list
695 else
696 private_list.append(timer_list_remove(timer));
697 }
698
699 // now re-insert them; this effectively re-sorts them by time
700 emu_timer *timer;
701 while ((timer = private_list.detach_head()) != nullptr)
702 timer_list_insert(*timer);
703
704 m_suspend_changes_pending = true;
705 rebuild_execute_list();
706
707 // report the timer state after a log
708 LOG("After resetting/reordering timers:\n");
709 #if VERBOSE
710 dump_timers();
711 #endif
712 }
713
714
715 //-------------------------------------------------
716 // compute_perfect_interleave - compute the
717 // "perfect" interleave interval
718 //-------------------------------------------------
719
compute_perfect_interleave()720 void device_scheduler::compute_perfect_interleave()
721 {
722 // ensure we have a list of executing devices
723 if (m_execute_list == nullptr)
724 rebuild_execute_list();
725
726 // start with the first one
727 device_execute_interface *first = m_execute_list;
728 if (first != nullptr)
729 {
730 // start with a huge time factor and find the 2nd smallest cycle time
731 attoseconds_t smallest = first->minimum_quantum();
732 attoseconds_t perfect = ATTOSECONDS_PER_SECOND - 1;
733 for (device_execute_interface *exec = first->m_nextexec; exec != nullptr; exec = exec->m_nextexec)
734 {
735 // find the 2nd smallest cycle interval
736 attoseconds_t curquantum = exec->minimum_quantum();
737 if (curquantum < smallest)
738 {
739 perfect = smallest;
740 smallest = curquantum;
741 }
742 else if (curquantum < perfect)
743 perfect = curquantum;
744 }
745
746 // if this is a new minimum quantum, apply it
747 if (m_quantum_minimum != perfect)
748 {
749 // adjust all the actuals; this doesn't affect the current
750 m_quantum_minimum = perfect;
751 for (quantum_slot &quant : m_quantum_list)
752 quant.m_actual = std::max(quant.m_requested, m_quantum_minimum);
753 }
754 }
755 }
756
757
758 //-------------------------------------------------
759 // rebuild_execute_list - rebuild the list of
760 // executing CPUs, moving suspended CPUs to the
761 // end
762 //-------------------------------------------------
763
rebuild_execute_list()764 void device_scheduler::rebuild_execute_list()
765 {
766 // if we haven't yet set a scheduling quantum, do it now
767 if (m_quantum_list.empty())
768 {
769 // set the core scheduling quantum, ensuring it's no longer than 60Hz
770 attotime min_quantum = machine().config().maximum_quantum(attotime::from_hz(60));
771
772 // if the configuration specifies a device to make perfect, pick that as the minimum
773 device_execute_interface *const exec(machine().config().perfect_quantum_device());
774 if (exec)
775 min_quantum = (std::min)(attotime(0, exec->minimum_quantum()), min_quantum);
776
777 // inform the timer system of our decision
778 add_scheduling_quantum(min_quantum, attotime::never);
779 }
780
781 // start with an empty list
782 device_execute_interface **active_tailptr = &m_execute_list;
783 *active_tailptr = nullptr;
784
785 // also make an empty list of suspended devices
786 device_execute_interface *suspend_list = nullptr;
787 device_execute_interface **suspend_tailptr = &suspend_list;
788
789 // iterate over all devices
790 for (device_execute_interface &exec : execute_interface_iterator(machine().root_device()))
791 {
792 // append to the appropriate list
793 exec.m_nextexec = nullptr;
794 if (exec.m_suspend == 0)
795 {
796 *active_tailptr = &exec;
797 active_tailptr = &exec.m_nextexec;
798 }
799 else
800 {
801 *suspend_tailptr = &exec;
802 suspend_tailptr = &exec.m_nextexec;
803 }
804 }
805
806 // append the suspend list to the end of the active list
807 *active_tailptr = suspend_list;
808 }
809
810
811 //-------------------------------------------------
812 // timer_list_insert - insert a new timer into
813 // the list at the appropriate location
814 //-------------------------------------------------
815
timer_list_insert(emu_timer & timer)816 inline emu_timer &device_scheduler::timer_list_insert(emu_timer &timer)
817 {
818 // disabled timers sort to the end
819 const attotime expire = timer.m_enabled ? timer.m_expire : attotime::never;
820
821 // loop over the timer list
822 emu_timer *prevtimer = nullptr;
823 for (emu_timer *curtimer = m_timer_list; curtimer != nullptr; prevtimer = curtimer, curtimer = curtimer->next())
824 {
825 // if the current list entry expires after us, we should be inserted before it
826 if (curtimer->m_expire > expire)
827 {
828 // link the new guy in before the current list entry
829 timer.m_prev = prevtimer;
830 timer.m_next = curtimer;
831
832 if (prevtimer != nullptr)
833 prevtimer->m_next = &timer;
834 else
835 m_timer_list = &timer;
836
837 curtimer->m_prev = &timer;
838 return timer;
839 }
840 }
841
842 // need to insert after the last one
843 if (prevtimer != nullptr)
844 prevtimer->m_next = &timer;
845 else
846 m_timer_list = &timer;
847
848 timer.m_prev = prevtimer;
849 timer.m_next = nullptr;
850 return timer;
851 }
852
853
854 //-------------------------------------------------
855 // timer_list_remove - remove a timer from the
856 // linked list
857 //-------------------------------------------------
858
timer_list_remove(emu_timer & timer)859 inline emu_timer &device_scheduler::timer_list_remove(emu_timer &timer)
860 {
861 // remove it from the list
862 if (timer.m_prev != nullptr)
863 timer.m_prev->m_next = timer.m_next;
864 else
865 m_timer_list = timer.m_next;
866
867 if (timer.m_next != nullptr)
868 timer.m_next->m_prev = timer.m_prev;
869
870 return timer;
871 }
872
873
874 //-------------------------------------------------
875 // execute_timers - execute timers that are due
876 //-------------------------------------------------
877
execute_timers()878 inline void device_scheduler::execute_timers()
879 {
880 LOG("execute_timers: new=%s head->expire=%s\n", m_basetime.as_string(PRECISION), m_timer_list->m_expire.as_string(PRECISION));
881
882 // now process any timers that are overdue
883 while (m_timer_list->m_expire <= m_basetime)
884 {
885 // if this is a one-shot timer, disable it now
886 emu_timer &timer = *m_timer_list;
887 bool was_enabled = timer.m_enabled;
888 if (timer.m_period.is_zero() || timer.m_period.is_never())
889 timer.m_enabled = false;
890
891 // set the global state of which callback we're in
892 m_callback_timer_modified = false;
893 m_callback_timer = &timer;
894 m_callback_timer_expire_time = timer.m_expire;
895
896 // call the callback
897 if (was_enabled)
898 {
899 g_profiler.start(PROFILER_TIMER_CALLBACK);
900
901 if (!timer.m_callback.isnull())
902 {
903 if (timer.m_device != nullptr)
904 LOG("execute_timers: timer device %s timer %d\n", timer.m_device->tag(), timer.m_id);
905 else
906 LOG("execute_timers: timer callback %s\n", timer.m_callback.name());
907 timer.m_callback(timer.m_ptr, timer.m_param);
908 }
909
910 g_profiler.stop();
911 }
912
913 // reset or remove the timer, but only if it wasn't modified during the callback
914 if (!m_callback_timer_modified)
915 {
916 // if the timer is temporary, remove it now
917 if (timer.m_temporary)
918 m_timer_allocator.reclaim(timer.release());
919
920 // otherwise, reschedule it
921 else
922 timer.schedule_next_period();
923 }
924 }
925
926 // clear the callback timer global
927 m_callback_timer = nullptr;
928 }
929
930
931 //-------------------------------------------------
932 // add_scheduling_quantum - add a scheduling
933 // quantum; the smallest active one is the one
934 // that is in use
935 //-------------------------------------------------
936
add_scheduling_quantum(const attotime & quantum,const attotime & duration)937 void device_scheduler::add_scheduling_quantum(const attotime &quantum, const attotime &duration)
938 {
939 assert(quantum.seconds() == 0);
940
941 attotime curtime = time();
942 attotime expire = curtime + duration;
943 const attoseconds_t quantum_attos = quantum.attoseconds();
944
945 // figure out where to insert ourselves, expiring any quanta that are out-of-date
946 quantum_slot *insert_after = nullptr;
947 quantum_slot *next;
948 for (quantum_slot *quant = m_quantum_list.first(); quant != nullptr; quant = next)
949 {
950 // if this quantum is expired, nuke it
951 next = quant->next();
952 if (curtime >= quant->m_expire)
953 m_quantum_allocator.reclaim(m_quantum_list.detach(*quant));
954
955 // if this quantum is shorter than us, we need to be inserted afterwards
956 else if (quant->m_requested <= quantum_attos)
957 insert_after = quant;
958 }
959
960 // if we found an exact match, just take the maximum expiry time
961 if (insert_after != nullptr && insert_after->m_requested == quantum_attos)
962 insert_after->m_expire = std::max(insert_after->m_expire, expire);
963
964 // otherwise, allocate a new quantum and insert it after the one we picked
965 else
966 {
967 quantum_slot &quant = *m_quantum_allocator.alloc();
968 quant.m_requested = quantum_attos;
969 quant.m_actual = std::max(quantum_attos, m_quantum_minimum);
970 quant.m_expire = expire;
971 m_quantum_list.insert_after(quant, insert_after);
972 }
973 }
974
975
976 //-------------------------------------------------
977 // dump_timers - dump the current timer state
978 //-------------------------------------------------
979
dump_timers() const980 void device_scheduler::dump_timers() const
981 {
982 machine().logerror("=============================================\n");
983 machine().logerror("Timer Dump: Time = %15s\n", time().as_string(PRECISION));
984 for (emu_timer *timer = first_timer(); timer != nullptr; timer = timer->next())
985 timer->dump();
986 machine().logerror("=============================================\n");
987 }
988