summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/schedule.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/schedule.cpp')
-rw-r--r--src/emu/schedule.cpp605
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 &timeslice_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");
}