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 &timeslice_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