//void trigger(int trigid, const attotime &after = attotime::zero); //------------------------------------------------- // boost_interleave - temporarily boosts the // interleave factor //------------------------------------------------- public void boost_interleave(attotime timeslice_time, attotime boost_duration) { // ignore timeslices > 1 second if (timeslice_time.seconds() > 0) { return; } add_scheduling_quantum(timeslice_time, boost_duration); }
// timer helpers //------------------------------------------------- // timer_list_insert - insert a new timer into // the list at the appropriate location //------------------------------------------------- public emu_timer timer_list_insert(emu_timer timer) { // disabled timers sort to the end attotime expire = timer.enabled() ? timer.expire() : attotime.never; // loop over the timer list emu_timer prevtimer = null; for (emu_timer curtimer = m_timer_list; curtimer != null; prevtimer = curtimer, curtimer = curtimer.next()) { // if the current list entry expires after us, we should be inserted before it if (curtimer.expire() > expire) { // link the new guy in before the current list entry timer.prev_set(curtimer.prev()); timer.next_set(curtimer); if (curtimer.prev() != null) { curtimer.prev().next_set(timer); } else { m_timer_list = timer; } curtimer.prev_set(timer); return(timer); } } // need to insert after the last one if (prevtimer != null) { prevtimer.next_set(timer); } else { m_timer_list = timer; } timer.prev_set(prevtimer); timer.next_set(null); return(timer); }
attoseconds_t m_quantum_minimum; // duration of minimum quantum // construction/destruction //------------------------------------------------- // device_scheduler - constructor //------------------------------------------------- public device_scheduler(running_machine machine) { m_machine = machine; m_basetime = attotime.zero; m_callback_timer_expire_time = attotime.zero; m_suspend_changes_pending = true; m_quantum_minimum = attotime.ATTOSECONDS_IN_NSEC(1) / 1000; // append a single never-expiring timer so there is always one in the list //m_timer_list = m_timer_allocator.alloc().init(machine, null, null, true); //m_timer_list.adjust(attotime.never); // register global states machine.save().save_item(m_basetime, "m_basetime"); machine.save().register_presave(presave); machine.save().register_postload(postload); }
//------------------------------------------------- // add_scheduling_quantum - add a scheduling // quantum; the smallest active one is the one // that is in use //------------------------------------------------- void add_scheduling_quantum(attotime quantum, attotime duration) { assert(quantum.seconds() == 0); attotime curtime = time(); attotime expire = curtime + duration; attoseconds_t quantum_attos = quantum.attoseconds(); // figure out where to insert ourselves, expiring any quanta that are out-of-date quantum_slot insert_after = null; quantum_slot next; for (quantum_slot quant = m_quantum_list.first(); quant != null; quant = next) { // if this quantum is expired, nuke it next = quant.next(); if (curtime >= quant.expire()) { m_quantum_allocator.reclaim(m_quantum_list.detach(quant)); } // if this quantum is shorter than us, we need to be inserted afterwards else if (quant.requested() <= quantum_attos) { insert_after = quant; } } // if we found an exact match, just take the maximum expiry time if (insert_after != null && insert_after.requested() == quantum_attos) { insert_after.expire_set(attotime.Max(insert_after.expire(), expire)); } // otherwise, allocate a new quantum and insert it after the one we picked else { quantum_slot quant = m_quantum_allocator.alloc(); quant.requested_set(quantum_attos); quant.actual_set(Math.Max(quantum_attos, m_quantum_minimum)); quant.expire_set(expire); m_quantum_list.insert_after(quant, insert_after); } }
//------------------------------------------------- // rebuild_execute_list - rebuild the list of // executing CPUs, moving suspended CPUs to the // end //------------------------------------------------- void rebuild_execute_list() { // if we haven't yet set a scheduling quantum, do it now if (m_quantum_list.empty()) { // set the core scheduling quantum attotime min_quantum = machine().config().minimum_quantum; // if none specified default to 60Hz if (min_quantum.is_zero()) { min_quantum = attotime.from_hz(60); } // if the configuration specifies a device to make perfect, pick that as the minimum if (!machine().config().perfect_cpu_quantum().empty()) { device_t device = machine().root_device().subdevice(machine().config().perfect_cpu_quantum().c_str()); if (device == null) { fatalerror("Device '{0}' specified for perfect interleave is not present!\n", machine().config().perfect_cpu_quantum()); } device_execute_interface exec; if (!device.interface_(out exec)) { fatalerror("Device '{0}' specified for perfect interleave is not an executing device!\n", machine().config().perfect_cpu_quantum()); } min_quantum = attotime.Min(new attotime(0, exec.minimum_quantum()), min_quantum); } // make sure it's no higher than 60Hz min_quantum = attotime.Min(min_quantum, attotime.from_hz(60)); // inform the timer system of our decision add_scheduling_quantum(min_quantum, attotime.never); } // start with an empty list //device_execute_interface **active_tailptr = &m_execute_list; //*active_tailptr = NULL; // also make an empty list of suspended devices //device_execute_interface *suspend_list = NULL; //device_execute_interface **suspend_tailptr = &suspend_list; List <device_execute_interface> active_list = new List <device_execute_interface>(); List <device_execute_interface> suspend_list = new List <device_execute_interface>(); // iterate over all devices foreach (device_execute_interface exec in new execute_interface_iterator(machine().root_device())) { // append to the appropriate list exec.nextexec = null; if (exec.suspend_ == 0) { //*active_tailptr = exec; //active_tailptr = &exec.m_nextexec; active_list.Add(exec); } else { //*suspend_tailptr = exec; //suspend_tailptr = &exec.m_nextexec; suspend_list.Add(exec); } } // append the suspend list to the end of the active list //*active_tailptr = suspend_list; active_list.AddRange(suspend_list); if (active_list.Count > 0) { m_execute_list = active_list[0]; for (int i = 0; i < active_list.Count; i++) { if (i < active_list.Count - 1) { active_list[i].nextexec = active_list[i + 1]; } else { active_list[i].nextexec = null; } } } }
//------------------------------------------------- // timer_set - allocate an anonymous device timer // and set it to go off after the given amount of // time //------------------------------------------------- public void timer_set(attotime duration, device_t device, device_timer_id id = 0, int param = 0, object ptr = null) { m_timer_allocator.alloc().init(device, id, ptr, true).adjust(duration, param); }
//------------------------------------------------- // timer_set - allocate an anonymous non-device // timer and set it to go off after the given // amount of time //------------------------------------------------- public void timer_set(attotime duration, timer_expired_delegate callback, int param = 0, object ptr = null) { m_timer_allocator.alloc().init(machine(), callback, ptr, true).adjust(duration, param); }
// execution //------------------------------------------------- // timeslice - execute all devices for a single // timeslice //------------------------------------------------- public void timeslice() { bool call_debugger = (machine().debug_flags_get & machine_global.DEBUG_FLAG_ENABLED) != 0; // build the execution list if we don't have one yet //if (UNEXPECTED(m_execute_list == null)) if (m_execute_list == null) { rebuild_execute_list(); } // if the current quantum has expired, find a new one while (m_basetime >= m_quantum_list.first().expire()) { m_quantum_allocator.reclaim(m_quantum_list.detach_head()); } // loop until we hit the next timer while (m_basetime < m_timer_list.expire()) { // by default, assume our target is the end of the next quantum attotime target = m_basetime + new attotime(0, m_quantum_list.first().actual()); // however, if the next timer is going to fire before then, override if (m_timer_list.expire() < target) { target = m_timer_list.expire(); } if (machine().video().frame_update_count() % 1000 == 0) { //LOG(("------------------\n")); LOG("device_scheduler.timeslice() - cpu_timeslice: target = {0}, m_timer_list.expire: {1}\n", target.as_string(), m_timer_list.expire().as_string()); } // do we have pending suspension changes? if (m_suspend_changes_pending) { apply_suspend_changes(); } // loop over all CPUs for (device_execute_interface exec = m_execute_list; exec != null; exec = exec.nextexec) { // only process if this CPU is executing or truly halted (not yielding) // and if our target is later than the CPU's current time (coarse check) //if (EXPECTED((exec.m_suspend == 0 || exec.m_eatcycles) && target.seconds >= exec.m_localtime.seconds)) if ((exec.suspend_ == 0 || exec.eatcycles > 0) && target.seconds() >= exec.localtime.seconds()) { // compute how many attoseconds to execute this CPU attoseconds_t delta = target.attoseconds() - exec.localtime.attoseconds(); if (delta < 0 && target.seconds() > exec.localtime.seconds()) { delta += attotime.ATTOSECONDS_PER_SECOND; } assert(delta == (target - exec.localtime).as_attoseconds()); if (exec.attoseconds_per_cycle == 0) { exec.localtime = target; } // if we have enough for at least 1 cycle, do the math else if (delta >= exec.attoseconds_per_cycle) { // compute how many cycles we want to execute int ran = exec.cycles_running = (int)divu_64x32((UInt64)delta >> exec.divshift, (UInt32)exec.divisor); if (machine().video().frame_update_count() % 1000 == 0) { LOG("device_scheduler.timeslice() - cpu '{0}': {1} ({2} cycles)\n", exec.device().tag(), delta, exec.cycles_running); } // if we're not suspended, actually execute if (exec.suspend_ == 0) { profiler_global.g_profiler.start(exec.profiler); // note that this global variable cycles_stolen can be modified // via the call to cpu_execute exec.cycles_stolen = 0; m_executing_device = exec; exec.icount_set(exec.cycles_running); // *exec->m_icountptr = exec->m_cycles_running; if (!call_debugger) { exec.run(); } else { exec.debugger_start_cpu_hook(target); exec.run(); exec.debugger_stop_cpu_hook(); } // adjust for any cycles we took back /*assert(ran >= *exec->m_icountptr);*/ ran -= exec.icountptrRef.i; /*assert(ran >= exec->m_cycles_stolen);*/ ran -= exec.cycles_stolen; profiler_global.g_profiler.stop(); } // account for these cycles exec.totalcycles += (UInt64)ran; // update the local time for this CPU attotime deltatime; if (ran < exec.cycles_per_second) { deltatime = new attotime(0, exec.attoseconds_per_cycle * ran); } else { UInt32 remainder; int secs = (int)divu_64x32_rem((UInt64)ran, exec.cycles_per_second, out remainder); deltatime = new attotime(secs, remainder * exec.attoseconds_per_cycle); } assert(deltatime >= attotime.zero); exec.localtime += deltatime; if (machine().video().frame_update_count() % 100 == 0) { LOG("device_scheduler.timeslice() - {0} ran, {1} total, time = {2}\n", ran, exec.totalcycles, exec.localtime.as_string()); } // if the new local CPU time is less than our target, move the target up, but not before the base if (exec.localtime < target) { target = attotime.Max(exec.localtime, m_basetime); if (machine().video().frame_update_count() % 1000 == 0) { LOG("device_scheduler.timeslice() - (new target)\n"); } } } } } m_executing_device = null; // update the base time m_basetime = target; } // execute timers execute_timers(); }
public void expire_set(attotime value) { m_expire = value; }
//------------------------------------------------- // execute_timers - execute timers that are due //------------------------------------------------- void execute_timers() { if (machine().video().frame_update_count() % 400 == 0) { LOG("execute_timers: new={0} head->expire={1}\n", m_basetime.as_string(), m_timer_list.expire().as_string()); } // now process any timers that are overdue while (m_timer_list.expire() <= m_basetime) { // if this is a one-shot timer, disable it now emu_timer timer = m_timer_list; bool was_enabled = timer.enabled(); if (timer.period().is_zero() || timer.period().is_never()) { timer.enabled_set(false); } // set the global state of which callback we're in m_callback_timer_modified = false; m_callback_timer = timer; m_callback_timer_expire_time = timer.expire(); // call the callback if (was_enabled) { profiler_global.g_profiler.start(profile_type.PROFILER_TIMER_CALLBACK); if (timer.device() != null) { if (machine().video().frame_update_count() % 400 == 0) { LOG("execute_timers: timer device {0} timer {1}\n", timer.device().tag(), timer.id()); } timer.device().timer_expired(timer, timer.id(), timer.param(), timer.ptr()); } else if (timer.callback() != null) { if (machine().video().frame_update_count() % 400 == 0) { LOG("execute_timers: timer callback {0}\n", timer.callback()); } timer.callback()(timer.ptr(), timer.param()); } profiler_global.g_profiler.stop(); } // clear the callback timer global m_callback_timer = null; // reset or remove the timer, but only if it wasn't modified during the callback if (!m_callback_timer_modified) { // if the timer is temporary, remove it now if (timer.temporary()) { m_timer_allocator.reclaim(timer.release()); } // otherwise, reschedule it else { timer.schedule_next_period(); } } } }