diff options
Diffstat (limited to 'src/emu/schedule.cpp')
-rw-r--r-- | src/emu/schedule.cpp | 605 |
1 files changed, 304 insertions, 301 deletions
diff --git a/src/emu/schedule.cpp b/src/emu/schedule.cpp index e2b1497995f..a475fd9b515 100644 --- a/src/emu/schedule.cpp +++ b/src/emu/schedule.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - schedule.c + schedule.cpp Core device execution and scheduling engine. @@ -17,7 +17,7 @@ #define VERBOSE 0 -#define LOG(x) do { if (VERBOSE) machine().logerror x; } while (0) +#define LOG(...) do { if (VERBOSE) machine().logerror(__VA_ARGS__); } while (0) #define PRECISION @@ -44,19 +44,16 @@ enum // emu_timer - constructor //------------------------------------------------- -emu_timer::emu_timer() - : m_machine(nullptr), - m_next(nullptr), - m_prev(nullptr), - m_param(0), - m_ptr(nullptr), - m_enabled(false), - m_temporary(false), - m_period(attotime::zero), - m_start(attotime::zero), - m_expire(attotime::never), - m_device(nullptr), - m_id(0) +inline emu_timer::emu_timer() noexcept : + m_scheduler(nullptr), + m_next(nullptr), + m_prev(nullptr), + m_param(0), + m_enabled(false), + m_temporary(false), + m_period(attotime::zero), + m_start(attotime::zero), + m_expire(attotime::never) { } @@ -75,74 +72,35 @@ emu_timer::~emu_timer() // re-allocated as a non-device timer //------------------------------------------------- -emu_timer &emu_timer::init(running_machine &machine, timer_expired_delegate callback, void *ptr, bool temporary) +inline emu_timer &emu_timer::init( + running_machine &machine, + timer_expired_delegate &&callback, + attotime start_delay, + s32 param, + bool temporary) { // ensure the entire timer state is clean - m_machine = &machine; + m_scheduler = &machine.scheduler(); m_next = nullptr; m_prev = nullptr; - m_callback = callback; - m_param = 0; - m_ptr = ptr; - m_enabled = false; + m_callback = std::move(callback); + m_param = param; m_temporary = temporary; m_period = attotime::never; - m_start = machine.time(); - m_expire = attotime::never; - m_device = nullptr; - m_id = 0; - - // if we're not temporary, register ourselves with the save state system - if (!m_temporary) - register_save(); - - // insert into the list - machine.scheduler().timer_list_insert(*this); - return *this; -} - - -//------------------------------------------------- -// init - completely initialize the state when -// re-allocated as a device timer -//------------------------------------------------- -emu_timer &emu_timer::init(device_t &device, device_timer_id id, void *ptr, bool temporary) -{ - // ensure the entire timer state is clean - m_machine = &device.machine(); - m_next = nullptr; - m_prev = nullptr; - m_callback = timer_expired_delegate(); - m_param = 0; - m_ptr = ptr; - m_enabled = false; - m_temporary = temporary; - m_period = attotime::never; - m_start = machine().time(); - m_expire = attotime::never; - m_device = &device; - m_id = id; + m_start = m_scheduler->time(); + m_expire = m_start + start_delay; + m_enabled = !m_expire.is_never(); // if we're not temporary, register ourselves with the save state system if (!m_temporary) - register_save(); + register_save(machine.save()); // insert into the list - machine().scheduler().timer_list_insert(*this); - return *this; -} - + m_scheduler->timer_list_insert(*this); + if (this == m_scheduler->first_timer()) + m_scheduler->abort_timeslice(); -//------------------------------------------------- -// release - release us from the global list -// management when deallocating -//------------------------------------------------- - -emu_timer &emu_timer::release() -{ - // unhook us from the global list - machine().scheduler().timer_list_remove(*this); return *this; } @@ -151,18 +109,20 @@ emu_timer &emu_timer::release() // enable - enable/disable a timer //------------------------------------------------- -bool emu_timer::enable(bool enable) +bool emu_timer::enable(bool enable) noexcept { + assert(m_scheduler); + // reschedule only if the state has changed - bool old = m_enabled; + const bool old = m_enabled; if (old != enable) { // set the enable flag m_enabled = enable; // remove the timer and insert back into the list - machine().scheduler().timer_list_remove(*this); - machine().scheduler().timer_list_insert(*this); + m_scheduler->timer_list_remove(*this); + m_scheduler->timer_list_insert(*this); } return old; } @@ -174,12 +134,13 @@ bool emu_timer::enable(bool enable) // firings //------------------------------------------------- -void emu_timer::adjust(attotime start_delay, s32 param, const attotime &period) +void emu_timer::adjust(attotime start_delay, s32 param, const attotime &period) noexcept { + assert(m_scheduler); + // if this is the callback timer, mark it modified - device_scheduler &scheduler = machine().scheduler(); - if (scheduler.m_callback_timer == this) - scheduler.m_callback_timer_modified = true; + if (m_scheduler->m_callback_timer == this) + m_scheduler->m_callback_timer_modified = true; // compute the time of the next firing and insert into the list m_param = param; @@ -190,17 +151,17 @@ void emu_timer::adjust(attotime start_delay, s32 param, const attotime &period) start_delay = attotime::zero; // set the start and expire times - m_start = scheduler.time(); + m_start = m_scheduler->time(); m_expire = m_start + start_delay; m_period = period; // remove and re-insert the timer in its new order - scheduler.timer_list_remove(*this); - scheduler.timer_list_insert(*this); + m_scheduler->timer_list_remove(*this); + m_scheduler->timer_list_insert(*this); // if this was inserted as the head, abort the current timeslice and resync - if (this == scheduler.first_timer()) - scheduler.abort_timeslice(); + if (this == m_scheduler->first_timer()) + m_scheduler->abort_timeslice(); } @@ -209,9 +170,11 @@ void emu_timer::adjust(attotime start_delay, s32 param, const attotime &period) // timer was started //------------------------------------------------- -attotime emu_timer::elapsed() const +attotime emu_timer::elapsed() const noexcept { - return machine().time() - m_start; + assert(m_scheduler); + + return m_scheduler->time() - m_start; } @@ -220,9 +183,11 @@ attotime emu_timer::elapsed() const // remaining until the timer expires //------------------------------------------------- -attotime emu_timer::remaining() const +attotime emu_timer::remaining() const noexcept { - attotime curtime = machine().time(); + assert(m_scheduler); + + const attotime curtime = m_scheduler->time(); if (curtime >= m_expire) return attotime::zero; return m_expire - curtime; @@ -234,41 +199,38 @@ attotime emu_timer::remaining() const // state system //------------------------------------------------- -void emu_timer::register_save() +void emu_timer::register_save(save_manager &manager) { - // determine our instance number and name + // determine our instance number - timers are indexed based on the callback function name int index = 0; - std::string name; - - // for non-device timers, it is an index based on the callback function name - if (m_device == nullptr) + std::string name = m_callback.name() ? m_callback.name() : "unnamed"; + for (const emu_timer *curtimer = m_scheduler->first_timer(); curtimer; curtimer = curtimer->m_next) { - name = m_callback.name() ? m_callback.name() : "unnamed"; - for (emu_timer *curtimer = machine().scheduler().first_timer(); curtimer != nullptr; curtimer = curtimer->next()) - if (!curtimer->m_temporary && curtimer->m_device == nullptr) - { - if (curtimer->m_callback.name() != nullptr && m_callback.name() != nullptr && strcmp(curtimer->m_callback.name(), m_callback.name()) == 0) - index++; - else if (curtimer->m_callback.name() == nullptr && m_callback.name() == nullptr) - index++; - } + if (!curtimer->m_temporary) + { + if (curtimer->m_callback.name() && m_callback.name() && !strcmp(curtimer->m_callback.name(), m_callback.name())) + index++; + else if (!curtimer->m_callback.name() && !m_callback.name()) + index++; + } } - - // for device timers, it is an index based on the device and timer ID - else + for (const emu_timer *curtimer = m_scheduler->m_inactive_timers; curtimer; curtimer = curtimer->m_next) { - name = string_format("%s/%d", m_device->tag(), m_id); - for (emu_timer *curtimer = machine().scheduler().first_timer(); curtimer != nullptr; curtimer = curtimer->next()) - if (!curtimer->m_temporary && curtimer->m_device != nullptr && curtimer->m_device == m_device && curtimer->m_id == m_id) - index++; + assert(!curtimer->m_temporary); + + if (curtimer->m_callback.name() && m_callback.name() && !strcmp(curtimer->m_callback.name(), m_callback.name())) + index++; + else if (!curtimer->m_callback.name() && !m_callback.name()) + index++; } // save the bits - machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_param)); - machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_enabled)); - machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_period)); - machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_start)); - machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_expire)); + manager.save_item(nullptr, "timer", name.c_str(), index, NAME(m_param)); + manager.save_item(nullptr, "timer", name.c_str(), index, NAME(m_enabled)); + manager.save_item(nullptr, "timer", name.c_str(), index, NAME(m_period)); + manager.save_item(nullptr, "timer", name.c_str(), index, NAME(m_start)); + manager.save_item(nullptr, "timer", name.c_str(), index, NAME(m_expire)); + manager.save_item(nullptr, "timer", name.c_str(), index, NAME(m_index)); } @@ -277,16 +239,17 @@ void emu_timer::register_save() // period //------------------------------------------------- -inline void emu_timer::schedule_next_period() +inline void emu_timer::schedule_next_period() noexcept { + assert(m_scheduler); + // advance by one period m_start = m_expire; m_expire += m_period; // remove and re-insert us - device_scheduler &scheduler = machine().scheduler(); - scheduler.timer_list_remove(*this); - scheduler.timer_list_insert(*this); + m_scheduler->timer_list_remove(*this); + m_scheduler->timer_list_insert(*this); } @@ -297,14 +260,13 @@ inline void emu_timer::schedule_next_period() void emu_timer::dump() const { - 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); - if (m_device == nullptr) - if (m_callback.name() == nullptr) - machine().logerror(" cb=NULL\n"); - else - machine().logerror(" cb=%s\n", m_callback.name()); + assert(m_scheduler); + + m_scheduler->machine().logerror("%p: en=%d temp=%d exp=%15s start=%15s per=%15s param=%d", this, m_enabled, m_temporary, m_expire.as_string(PRECISION), m_start.as_string(PRECISION), m_period.as_string(PRECISION), m_param); + if (!m_callback.name()) + m_scheduler->machine().logerror(" cb=NULL\n"); else - machine().logerror(" dev=%s id=%d\n", m_device->tag(), m_id); + m_scheduler->machine().logerror(" cb=%s\n", m_callback.name()); } @@ -323,6 +285,7 @@ device_scheduler::device_scheduler(running_machine &machine) : m_execute_list(nullptr), m_basetime(attotime::zero), m_timer_list(nullptr), + m_inactive_timers(nullptr), m_callback_timer(nullptr), m_callback_timer_modified(false), m_callback_timer_expire_time(attotime::zero), @@ -330,8 +293,13 @@ device_scheduler::device_scheduler(running_machine &machine) : m_quantum_minimum(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, timer_expired_delegate(), nullptr, true); - m_timer_list->adjust(attotime::never); + // need to subvert it because it would naturally be inserted in the inactive list + m_timer_list = &timer_list_remove(m_timer_allocator.alloc()->init(machine, timer_expired_delegate(), attotime::never, 0, true)); + + assert(m_timer_list); + assert(!m_timer_list->m_prev); + assert(!m_timer_list->m_next); + assert(!m_inactive_timers); // register global states machine.save().save_item(NAME(m_basetime)); @@ -347,8 +315,10 @@ device_scheduler::device_scheduler(running_machine &machine) : device_scheduler::~device_scheduler() { // remove all timers - while (m_timer_list != nullptr) - m_timer_allocator.reclaim(m_timer_list->release()); + while (m_inactive_timers) + m_timer_allocator.reclaim(timer_list_remove(*m_inactive_timers)); + while (m_timer_list) + m_timer_allocator.reclaim(timer_list_remove(*m_timer_list)); } @@ -356,7 +326,7 @@ device_scheduler::~device_scheduler() // time - return the current time //------------------------------------------------- -attotime device_scheduler::time() const +attotime device_scheduler::time() const noexcept { // if we're currently in a callback, use the timer's expiration time as a base if (m_callback_timer != nullptr) @@ -376,13 +346,15 @@ attotime device_scheduler::time() const bool device_scheduler::can_save() const { // if any live temporary timers exit, fail - for (emu_timer *timer = m_timer_list; timer != nullptr; timer = timer->next()) + for (emu_timer *timer = m_timer_list; timer; timer = timer->m_next) + { if (timer->m_temporary && !timer->expire().is_never()) { machine().logerror("Failed save state attempt due to anonymous timers:\n"); dump_timers(); return false; } + } // otherwise, we're good return true; @@ -440,8 +412,8 @@ void device_scheduler::timeslice() if (m_timer_list->m_expire < target) target = m_timer_list->m_expire; - LOG(("------------------\n")); - LOG(("cpu_timeslice: target = %s\n", target.as_string(PRECISION))); + LOG("------------------\n"); + LOG("cpu_timeslice: target = %s\n", target.as_string(PRECISION)); // do we have pending suspension changes? if (m_suspend_changes_pending) @@ -469,12 +441,12 @@ void device_scheduler::timeslice() { // compute how many cycles we want to execute int ran = exec->m_cycles_running = divu_64x32(u64(delta) >> exec->m_divshift, exec->m_divisor); - LOG((" cpu '%s': %d (%d cycles)\n", exec->device().tag(), delta, exec->m_cycles_running)); + LOG(" cpu '%s': %d (%d cycles)\n", exec->device().tag(), delta, exec->m_cycles_running); // if we're not suspended, actually execute if (exec->m_suspend == 0) { - g_profiler.start(exec->m_profiler); + auto profile = g_profiler.start(exec->m_profiler); // note that this global variable cycles_stolen can be modified // via the call to cpu_execute @@ -495,7 +467,6 @@ void device_scheduler::timeslice() ran -= *exec->m_icountptr; assert(ran >= exec->m_cycles_stolen); ran -= exec->m_cycles_stolen; - g_profiler.stop(); } // account for these cycles @@ -503,23 +474,23 @@ void device_scheduler::timeslice() // update the local time for this CPU attotime deltatime; - if (ran < exec->m_cycles_per_second) + if (EXPECTED(ran < exec->m_cycles_per_second)) deltatime = attotime(0, exec->m_attoseconds_per_cycle * ran); else { u32 remainder; - s32 secs = divu_64x32_rem(ran, exec->m_cycles_per_second, &remainder); + s32 secs = divu_64x32_rem(ran, exec->m_cycles_per_second, remainder); deltatime = attotime(secs, u64(remainder) * exec->m_attoseconds_per_cycle); } assert(deltatime >= attotime::zero); exec->m_localtime += deltatime; - LOG((" %d ran, %d total, time = %s\n", ran, s32(exec->m_totalcycles), exec->m_localtime.as_string(PRECISION))); + LOG(" %d ran, %d total, time = %s\n", ran, s32(exec->m_totalcycles), exec->m_localtime.as_string(PRECISION)); // if the new local CPU time is less than our target, move the target up, but not before the base if (exec->m_localtime < target) { target = std::max(exec->m_localtime, m_basetime); - LOG((" (new target)\n")); + LOG(" (new target)\n"); } } } @@ -540,7 +511,7 @@ void device_scheduler::timeslice() // current timeslice //------------------------------------------------- -void device_scheduler::abort_timeslice() +void device_scheduler::abort_timeslice() noexcept { if (m_executing_device != nullptr) m_executing_device->abort_timeslice(); @@ -557,74 +528,117 @@ void device_scheduler::trigger(int trigid, const attotime &after) if (m_execute_list == nullptr) rebuild_execute_list(); - // if we have a non-zero time, schedule a timer if (after != attotime::zero) + { + // if we have a non-zero time, schedule a timer timer_set(after, timer_expired_delegate(FUNC(device_scheduler::timed_trigger), this), trigid); - - // send the trigger to everyone who cares + } else - for (device_execute_interface *exec = m_execute_list; exec != nullptr; exec = exec->m_nextexec) + { + // send the trigger to everyone who cares + for (device_execute_interface *exec = m_execute_list; exec; exec = exec->m_nextexec) exec->trigger(trigid); + } } //------------------------------------------------- -// boost_interleave - temporarily boosts the -// interleave factor +// add_quantum - add a scheduling quantum; +// the smallest active one is the one that is in use //------------------------------------------------- -void device_scheduler::boost_interleave(const attotime ×lice_time, const attotime &boost_duration) +void device_scheduler::add_quantum(const attotime &quantum, const attotime &duration) { - // ignore timeslices > 1 second - if (timeslice_time.seconds() > 0) - return; - add_scheduling_quantum(timeslice_time, boost_duration); + assert(quantum.seconds() == 0); + + attotime curtime = time(); + attotime expire = curtime + duration; + const attoseconds_t quantum_attos = quantum.attoseconds(); + + // figure out where to insert ourselves, expiring any quanta that are out-of-date + quantum_slot *insert_after = nullptr; + quantum_slot *next; + for (quantum_slot *quant = m_quantum_list.first(); quant != nullptr; quant = next) + { + // if this quantum is expired, nuke it + next = quant->next(); + if (curtime >= quant->m_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->m_requested <= quantum_attos) + insert_after = quant; + } + + // if we found an exact match, just take the maximum expiry time + if (insert_after != nullptr && insert_after->m_requested == quantum_attos) + insert_after->m_expire = std::max(insert_after->m_expire, expire); + + // otherwise, allocate a new quantum and insert it after the one we picked + else + { + quantum_slot &quant = *m_quantum_allocator.alloc(); + quant.m_requested = quantum_attos; + quant.m_actual = std::max(quantum_attos, m_quantum_minimum); + quant.m_expire = expire; + m_quantum_list.insert_after(quant, insert_after); + } } //------------------------------------------------- -// timer_alloc - allocate a global non-device -// timer and return a pointer +// perfect_quantum - add a (temporary) minimum +// scheduling quantum to boost the interleave //------------------------------------------------- -emu_timer *device_scheduler::timer_alloc(timer_expired_delegate callback, void *ptr) +void device_scheduler::perfect_quantum(const attotime &duration) { - return &m_timer_allocator.alloc()->init(machine(), callback, ptr, false); + add_quantum(attotime::zero, duration); } //------------------------------------------------- -// timer_set - allocate an anonymous non-device -// timer and set it to go off after the given -// amount of time +// timer_alloc - allocate a global non-device +// timer and return a pointer //------------------------------------------------- -void device_scheduler::timer_set(const attotime &duration, timer_expired_delegate callback, int param, void *ptr) +emu_timer *device_scheduler::timer_alloc(timer_expired_delegate callback) { - m_timer_allocator.alloc()->init(machine(), callback, ptr, true).adjust(duration, param); + return &m_timer_allocator.alloc()->init(machine(), std::move(callback), attotime::never, 0, false); } //------------------------------------------------- -// timer_alloc - allocate a global device timer -// and return a pointer +// timer_set - allocate an anonymous non-device +// timer and set it to go off after the given +// amount of time //------------------------------------------------- -emu_timer *device_scheduler::timer_alloc(device_t &device, device_timer_id id, void *ptr) +void device_scheduler::timer_set(const attotime &duration, timer_expired_delegate callback, s32 param) { - return &m_timer_allocator.alloc()->init(device, id, ptr, false); + [[maybe_unused]] emu_timer &timer = m_timer_allocator.alloc()->init( + machine(), + std::move(callback), + duration, + param, + true); + assert(!timer.m_expire.is_never()); // this is not handled } //------------------------------------------------- -// timer_set - allocate an anonymous device timer -// and set it to go off after the given amount of -// time +// synchronize - allocate an anonymous non-device +// timer and set it to go off as soon as possible //------------------------------------------------- -void device_scheduler::timer_set(const attotime &duration, device_t &device, device_timer_id id, int param, void *ptr) +void device_scheduler::synchronize(timer_expired_delegate callback, s32 param) { - m_timer_allocator.alloc()->init(device, id, ptr, true).adjust(duration, param); + m_timer_allocator.alloc()->init( + machine(), + std::move(callback), + attotime::zero, + param, + true); } @@ -645,7 +659,7 @@ void device_scheduler::eat_all_cycles() // given amount of time //------------------------------------------------- -void device_scheduler::timed_trigger(void *ptr, s32 param) +void device_scheduler::timed_trigger(s32 param) { trigger(param); } @@ -658,7 +672,10 @@ void device_scheduler::timed_trigger(void *ptr, s32 param) void device_scheduler::presave() { // report the timer state after a log - LOG(("Prior to saving state:\n")); + LOG("Prior to saving state:\n"); + u32 index = 0; + for (emu_timer *timer = m_timer_list; timer; timer = timer->m_next) + timer->m_index = index++; #if VERBOSE dump_timers(); #endif @@ -672,30 +689,53 @@ void device_scheduler::presave() void device_scheduler::postload() { // remove all timers and make a private list of permanent ones - simple_list<emu_timer> private_list; - while (m_timer_list != nullptr) + emu_timer *private_list = nullptr; + while (m_inactive_timers) + { + emu_timer &timer = *m_inactive_timers; + assert(!timer.m_temporary); + + timer_list_remove(timer).m_next = private_list; + private_list = &timer; + } + while (m_timer_list->m_next) { emu_timer &timer = *m_timer_list; - // temporary timers go away entirely (except our special never-expiring one) - if (timer.m_temporary && !timer.expire().is_never()) - m_timer_allocator.reclaim(timer.release()); + if (timer.m_temporary) + { + assert(!timer.expire().is_never()); - // permanent ones get added to our private list + // temporary timers go away entirely (except our special never-expiring one) + timer.m_callback.reset(); + m_timer_allocator.reclaim(timer_list_remove(timer)); + } else - private_list.append(timer_list_remove(timer)); + { + // permanent ones get added to our private list + timer_list_remove(timer).m_next = private_list; + private_list = &timer; + } } + // special dummy timer + assert(!m_timer_list->m_enabled); + assert(m_timer_list->m_temporary); + assert(m_timer_list->m_expire.is_never()); + // now re-insert them; this effectively re-sorts them by time - emu_timer *timer; - while ((timer = private_list.detach_head()) != nullptr) - timer_list_insert(*timer); + while (private_list) + { + emu_timer &timer = *private_list; + private_list = timer.m_next; + timer_list_insert<true>(timer); + } m_suspend_changes_pending = true; rebuild_execute_list(); // report the timer state after a log - LOG(("After resetting/reordering timers:\n")); + LOG("After resetting/reordering timers:\n"); #if VERBOSE dump_timers(); #endif @@ -756,32 +796,16 @@ void device_scheduler::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().m_minimum_quantum; - - // if none specified default to 60Hz - if (min_quantum.is_zero()) - min_quantum = attotime::from_hz(60); + // set the core scheduling quantum, ensuring it's no longer than 60Hz + attotime min_quantum = machine().config().maximum_quantum(attotime::from_hz(60)); // if the configuration specifies a device to make perfect, pick that as the minimum - if (!machine().config().m_perfect_cpu_quantum.empty()) - { - device_t *device = machine().root_device().subdevice(machine().config().m_perfect_cpu_quantum.c_str()); - if (device == nullptr) - fatalerror("Device '%s' specified for perfect interleave is not present!\n", machine().config().m_perfect_cpu_quantum.c_str()); - - device_execute_interface *exec; - if (!device->interface(exec)) - fatalerror("Device '%s' specified for perfect interleave is not an executing device!\n", machine().config().m_perfect_cpu_quantum.c_str()); - - min_quantum = std::min(attotime(0, exec->minimum_quantum()), min_quantum); - } - - // make sure it's no higher than 60Hz - min_quantum = std::min(min_quantum, attotime::from_hz(60)); + device_execute_interface *const exec(machine().config().perfect_quantum_device()); + if (exec) + min_quantum = (std::min)(attotime(0, exec->minimum_quantum()), min_quantum); // inform the timer system of our decision - add_scheduling_quantum(min_quantum, attotime::never); + add_quantum(min_quantum, attotime::never); } // start with an empty list @@ -793,7 +817,7 @@ void device_scheduler::rebuild_execute_list() device_execute_interface **suspend_tailptr = &suspend_list; // iterate over all devices - for (device_execute_interface &exec : execute_interface_iterator(machine().root_device())) + for (device_execute_interface &exec : execute_interface_enumerator(machine().root_device())) { // append to the appropriate list exec.m_nextexec = nullptr; @@ -819,40 +843,56 @@ void device_scheduler::rebuild_execute_list() // the list at the appropriate location //------------------------------------------------- -emu_timer &device_scheduler::timer_list_insert(emu_timer &timer) +template <bool CheckIndex> +inline emu_timer &device_scheduler::timer_list_insert(emu_timer &timer) { - // disabled timers sort to the end - const attotime &expire = timer.m_enabled ? timer.m_expire : attotime::never; - - // loop over the timer list - emu_timer *prevtimer = nullptr; - for (emu_timer *curtimer = m_timer_list; curtimer != nullptr; prevtimer = curtimer, curtimer = curtimer->next()) + // disabled timers never expire + if (!timer.m_expire.is_never() && timer.m_enabled) { - // if the current list entry expires after us, we should be inserted before it - if (curtimer->m_expire > expire) + // loop over the timer list + emu_timer *prevtimer = nullptr; + for (emu_timer *curtimer = m_timer_list; curtimer; prevtimer = curtimer, curtimer = curtimer->m_next) { - // link the new guy in before the current list entry - timer.m_prev = curtimer->m_prev; - timer.m_next = curtimer; + // if the current list entry expires after us, we should be inserted before it + bool const here = + (curtimer->m_expire > timer.m_expire) || + (CheckIndex && !(curtimer->m_expire < timer.m_expire) && (curtimer->m_index > timer.m_index)); + if (here) + { + // link the new guy in before the current list entry + timer.m_prev = prevtimer; + timer.m_next = curtimer; - if (curtimer->m_prev != nullptr) - curtimer->m_prev->m_next = &timer; - else - m_timer_list = &timer; + if (prevtimer) + prevtimer->m_next = &timer; + else + m_timer_list = &timer; - curtimer->m_prev = &timer; - return timer; + curtimer->m_prev = &timer; + return timer; + } } - } - // need to insert after the last one - if (prevtimer != nullptr) - prevtimer->m_next = &timer; + // need to insert after the last one + if (prevtimer) + prevtimer->m_next = &timer; + else + m_timer_list = &timer; + + timer.m_prev = prevtimer; + timer.m_next = nullptr; + } else - m_timer_list = &timer; + { + // keep inactive timers in a separate list + if (m_inactive_timers) + m_inactive_timers->m_prev = &timer; + + timer.m_next = m_inactive_timers; + timer.m_prev = nullptr; - timer.m_prev = prevtimer; - timer.m_next = nullptr; + m_inactive_timers = &timer; + } return timer; } @@ -862,15 +902,24 @@ emu_timer &device_scheduler::timer_list_insert(emu_timer &timer) // linked list //------------------------------------------------- -emu_timer &device_scheduler::timer_list_remove(emu_timer &timer) +inline emu_timer &device_scheduler::timer_list_remove(emu_timer &timer) { // remove it from the list - if (timer.m_prev != nullptr) + if (timer.m_prev) + { timer.m_prev->m_next = timer.m_next; - else + } + else if (&timer == m_timer_list) + { m_timer_list = timer.m_next; + } + else + { + assert(&timer == m_inactive_timers); + m_inactive_timers = timer.m_next; + } - if (timer.m_next != nullptr) + if (timer.m_next) timer.m_next->m_prev = timer.m_prev; return timer; @@ -883,7 +932,7 @@ emu_timer &device_scheduler::timer_list_remove(emu_timer &timer) inline void device_scheduler::execute_timers() { - LOG(("execute_timers: new=%s head->expire=%s\n", m_basetime.as_string(PRECISION), m_timer_list->m_expire.as_string(PRECISION))); + LOG("execute_timers: new=%s head->expire=%s\n", m_basetime.as_string(PRECISION), m_timer_list->m_expire.as_string(PRECISION)); // now process any timers that are overdue while (m_timer_list->m_expire <= m_basetime) @@ -902,82 +951,34 @@ inline void device_scheduler::execute_timers() // call the callback if (was_enabled) { - g_profiler.start(PROFILER_TIMER_CALLBACK); + auto profile = g_profiler.start(PROFILER_TIMER_CALLBACK); - if (timer.m_device != nullptr) + if (!timer.m_callback.isnull()) { - LOG(("execute_timers: timer device %s timer %d\n", timer.m_device->tag(), timer.m_id)); - timer.m_device->timer_expired(timer, timer.m_id, timer.m_param, timer.m_ptr); + LOG("execute_timers: timer callback %s\n", timer.m_callback.name()); + timer.m_callback(timer.m_param); } - else if (!timer.m_callback.isnull()) - { - LOG(("execute_timers: timer callback %s\n", timer.m_callback.name())); - timer.m_callback(timer.m_ptr, timer.m_param); - } - - g_profiler.stop(); } - // clear the callback timer global - m_callback_timer = nullptr; - // 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.m_temporary) - m_timer_allocator.reclaim(timer.release()); - - // otherwise, reschedule it - else + if (!timer.m_temporary) + { + // if the timer is not temporary, reschedule it timer.schedule_next_period(); + } + else + { + // otherwise, remove it now + timer.m_callback.reset(); + m_timer_allocator.reclaim(timer_list_remove(timer)); + } } } -} - - -//------------------------------------------------- -// add_scheduling_quantum - add a scheduling -// quantum; the smallest active one is the one -// that is in use -//------------------------------------------------- - -void device_scheduler::add_scheduling_quantum(const attotime &quantum, const attotime &duration) -{ - assert(quantum.seconds() == 0); - - attotime curtime = time(); - attotime expire = curtime + duration; - const attoseconds_t quantum_attos = quantum.attoseconds(); - - // figure out where to insert ourselves, expiring any quanta that are out-of-date - quantum_slot *insert_after = nullptr; - quantum_slot *next; - for (quantum_slot *quant = m_quantum_list.first(); quant != nullptr; quant = next) - { - // if this quantum is expired, nuke it - next = quant->next(); - if (curtime >= quant->m_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->m_requested <= quantum_attos) - insert_after = quant; - } - // if we found an exact match, just take the maximum expiry time - if (insert_after != nullptr && insert_after->m_requested == quantum_attos) - insert_after->m_expire = std::max(insert_after->m_expire, expire); - - // otherwise, allocate a new quantum and insert it after the one we picked - else - { - quantum_slot &quant = *m_quantum_allocator.alloc(); - quant.m_requested = quantum_attos; - quant.m_actual = std::max(quantum_attos, m_quantum_minimum); - quant.m_expire = expire; - m_quantum_list.insert_after(quant, insert_after); - } + // clear the callback timer global + m_callback_timer = nullptr; } @@ -989,7 +990,9 @@ void device_scheduler::dump_timers() const { machine().logerror("=============================================\n"); machine().logerror("Timer Dump: Time = %15s\n", time().as_string(PRECISION)); - for (emu_timer *timer = first_timer(); timer != nullptr; timer = timer->next()) + for (emu_timer *timer = m_timer_list; timer; timer = timer->m_next) + timer->dump(); + for (emu_timer *timer = m_inactive_timers; timer; timer = timer->m_next) timer->dump(); machine().logerror("=============================================\n"); } |