summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2021-03-29 18:24:49 -0700
committer Aaron Giles <aaron@aarongiles.com>2021-03-29 18:24:49 -0700
commit92e9d1e8928a0fbe7ccfb9f053b2434e2dae9d9c (patch)
treee2a7dc431aaf411ff7df3129b94254ece609871d
parent3a6239dd46f846e83604f25acc37bc8c83a2f034 (diff)
Change the way timers are saved. Use presave to populate a fixed array of data, and postsave to restore it, rather than saving each timer. Add a device_persistent_timer to accept device parameters and keep those details out of schedule.cpp. Device timers now using parameter 2 to store the ID, rather than the ID being special.
-rw-r--r--src/devices/machine/phi.cpp2
-rw-r--r--src/emu/device.h41
-rw-r--r--src/emu/emu.h2
-rw-r--r--src/emu/schedule.cpp694
-rw-r--r--src/emu/schedule.h312
-rw-r--r--src/emu/screen.h4
6 files changed, 639 insertions, 416 deletions
diff --git a/src/devices/machine/phi.cpp b/src/devices/machine/phi.cpp
index b53c9783e7a..1121ff73ab0 100644
--- a/src/devices/machine/phi.cpp
+++ b/src/devices/machine/phi.cpp
@@ -419,7 +419,7 @@ void phi_device::device_reset()
void phi_device::device_timer(timer_instance const &timer, device_timer_id id, int param, void *ptr)
{
- LOG_NOISY("tmr %d enabled %d\n" , id , timer.enabled());
+ LOG_NOISY("tmr %d\n" , id);
update_fsm();
}
diff --git a/src/emu/device.h b/src/emu/device.h
index 6c5bc61f29a..9e4360923c8 100644
--- a/src/emu/device.h
+++ b/src/emu/device.h
@@ -384,6 +384,7 @@ class device_t : public delegate_late_bind
DISABLE_COPYING(device_t);
friend class simple_list<device_t>;
+ friend class device_persistent_timer;
friend class running_machine;
friend class finder_base;
friend class devcb_base;
@@ -614,15 +615,14 @@ public:
{
return m_scheduler->timer_alloc(*this, id, ptr);
}
- void timer_set(const attotime &duration, device_timer_id id = 0, int param = 0)
+ void timer_set(const attotime &duration, device_timer_id id = 0, u64 param = 0, u64 param2 = 0)
{
- m_device_timer.call_after(id, duration, param);
+ m_device_timer.call_after(duration, param, param2, id);
}
- void synchronize(device_timer_id id, int param = 0)
+ void synchronize(device_timer_id id, u64 param = 0, u64 param2 = 0)
{
- timer_set(attotime::zero, id, param);
+ m_device_timer.call_after(attotime::zero, param, param2, id);
}
- transient_timer_factory &device_timer_factory() { return m_device_timer; }
// state saving interfaces
template<typename ItemType>
@@ -1343,6 +1343,37 @@ private:
};
+// ======================> device_persistent_timer
+
+class device_persistent_timer : public persistent_timer
+{
+public:
+ // construction/destruction
+ device_persistent_timer() :
+ persistent_timer() { }
+
+ // initialize with a device and ID; implicitly calls the device's device_timer method
+ persistent_timer &init(device_t &device, device_timer_id id, void *ptr = nullptr)
+ {
+ // turn the ID into a string as an additional uniquifier
+ char tempstr[64];
+ sprintf(tempstr, "%d", id);
+
+ // call through to the base class to register this
+ persistent_timer::init(device, FUNC(device_t::device_timer), tempstr).set_param(2, id);
+ m_callback.set_ptr(ptr);
+ return *this;
+ }
+
+protected:
+ // hide the base class inits
+ using persistent_timer::init;
+
+ // device timers use param2 for the id, so hide the three-parameter form
+ persistent_timer &set_params(u64 param0, u64 param1, u64 param2) { return *this; }
+};
+
+
//**************************************************************************
// INLINE FUNCTIONS
diff --git a/src/emu/emu.h b/src/emu/emu.h
index 5497437c9e6..ba606399ce9 100644
--- a/src/emu/emu.h
+++ b/src/emu/emu.h
@@ -45,7 +45,6 @@
#include "fileio.h"
#include "delegate.h"
#include "devdelegate.h"
-#include "schedule.h"
// memory and address spaces
#include "emumem.h"
@@ -53,6 +52,7 @@
// machine-wide utilities
#include "romentry.h"
#include "save.h"
+#include "schedule.h"
// I/O
#include "input.h"
diff --git a/src/emu/schedule.cpp b/src/emu/schedule.cpp
index 5e2437ec7de..dd7fcf11d88 100644
--- a/src/emu/schedule.cpp
+++ b/src/emu/schedule.cpp
@@ -32,12 +32,14 @@
// timer_callback - constructor
//-------------------------------------------------
-timer_callback::timer_callback() :
+timer_callback::timer_callback(persistent_timer *persistent) :
m_ptr(nullptr),
- m_device(nullptr),
m_scheduler(nullptr),
- m_next(nullptr),
- m_unique_hash(0)
+ m_next_registered(nullptr),
+ m_persistent(persistent),
+ m_device(nullptr),
+ m_unique_hash(0),
+ m_save_index(0)
{
}
@@ -58,12 +60,14 @@ timer_callback::~timer_callback()
//-------------------------------------------------
timer_callback::timer_callback(timer_callback const &src) :
- m_callback(src.m_callback),
+ m_delegate(src.m_delegate),
m_ptr(src.m_ptr),
- m_device(src.m_device),
m_scheduler(src.m_scheduler),
- m_next(src.m_next),
+ m_next_registered(src.m_next_registered),
+ m_persistent(nullptr),
+ m_device(src.m_device),
m_unique_hash(src.m_unique_hash),
+ m_save_index(src.m_save_index),
m_unique_id(src.m_unique_id)
{
}
@@ -77,11 +81,13 @@ timer_callback &timer_callback::operator=(timer_callback const &src)
{
if (&src != this)
{
- m_callback = src.m_callback;
+ m_delegate = src.m_delegate;
m_ptr = src.m_ptr;
m_device = src.m_device;
m_scheduler = src.m_scheduler;
- m_next = src.m_next;
+ m_next_registered = src.m_next_registered;
+ // deliberately do not touch m_persistent since it is
+ // an allocation-only property
m_unique_hash = src.m_unique_hash;
m_unique_id = src.m_unique_id;
}
@@ -90,33 +96,71 @@ timer_callback &timer_callback::operator=(timer_callback const &src)
//-------------------------------------------------
+// set_ptr - set the callback's pointer value;
+// only valid at initialization
+//-------------------------------------------------
+
+timer_callback &timer_callback::set_ptr(void *ptr)
+{
+ // only allowed to set pointers prior to execution; use the save state
+ // registration_allowed() as a proxy for this
+ if (!m_scheduler->machine().save().registration_allowed())
+ throw emu_fatalerror("Timer pointers must remain constant after creation.");
+ m_ptr = ptr;
+ return *this;
+}
+
+
+//-------------------------------------------------
+// set_device - set the callback's associated
+// device; only valid at initialization
+//-------------------------------------------------
+
+timer_callback &timer_callback::set_device(device_t &device)
+{
+ // only allowed to set pointers prior to execution; use the save state
+ // registration_allowed() as a proxy for this
+ if (!m_scheduler->machine().save().registration_allowed())
+ throw emu_fatalerror("Timer devices must remain constant after creation.");
+ m_device = &device;
+ return *this;
+}
+
+
+//-------------------------------------------------
// enregister_base - register a callback
//-------------------------------------------------
-timer_callback &timer_callback::enregister_base(device_scheduler &scheduler, timer_expired_delegate const &callback, char const *unique)
+timer_callback &timer_callback::enregister_base(device_scheduler &scheduler, timer_expired_delegate const &callback, char const *unique, const char *unique2)
{
- // build the full name, appending the unique identifier if present
+ // build the full name, appending the unique identifier(s) if present
std::string fullid = callback.name();
if (unique != nullptr)
{
fullid += "/";
fullid += unique;
}
+ if (unique2 != nullptr)
+ {
+ fullid += "/";
+ fullid += unique2;
+ }
// if not already registered, just pass through
- if (m_next == nullptr)
+ if (m_next_registered == nullptr)
{
- m_callback = callback;
+ m_delegate = callback;
m_scheduler = &scheduler;
m_unique_id = fullid;
m_unique_hash = util::crc32_creator::simple(fullid.c_str(), fullid.length());
- m_scheduler->register_callback(*this);
+ m_save_index = m_scheduler->register_callback(*this);
+printf("Registered: %d %08X %s\n", m_save_index, m_unique_hash, m_unique_id.c_str());
}
// otherwise, make sure we match
else
{
- if (!(m_callback == callback))
+ if (!(m_delegate == callback))
throw emu_fatalerror("timer_callback::enregister called multiple times on the same object with different callbacks.");
if (m_unique_id != fullid)
throw emu_fatalerror("timer_callback::enregister called multiple times on the same object with different ids (%s vs. %s).", m_unique_id.c_str(), fullid.c_str());
@@ -132,16 +176,7 @@ timer_callback &timer_callback::enregister_base(device_scheduler &scheduler, tim
timer_callback &timer_callback::enregister_device(device_t &device, timer_expired_delegate const &callback, char const *unique)
{
- char const *new_unique = device.tag();
- std::string tempstr;
- if (unique != nullptr)
- {
- tempstr = device.tag();
- tempstr += "/";
- tempstr += unique;
- new_unique = tempstr.c_str();
- }
- return enregister_base(device.machine().scheduler(), callback, new_unique).set_device(device);
+ return enregister_base(device.machine().scheduler(), callback, device.tag(), unique).set_device(device);
}
@@ -178,23 +213,18 @@ timer_instance::~timer_instance()
timer_instance &timer_instance::init_persistent(timer_callback &callback)
{
- // ensure the entire timer state is clean
- m_start = callback.m_scheduler->time();
- m_expire = attotime::never;
- m_period = attotime::never;
+ assert(callback.persistent() != nullptr);
m_callback = &callback;
- m_param = 0;
- m_param2 = 0;
- m_param3 = 0;
- m_id = 0;
- m_enabled = false;
- m_transient = false;
- // register ourselves with the save state system
- register_save();
+ // ensure the entire timer state is clean
+ m_param[0] = m_param[1] = m_param[2] = 0;
+ m_active = false;
- // add to the inactive queue
- return callback.m_scheduler->insert_inactive(*this);
+ // set the start/expire times to defaults
+ m_start = attotime::zero;
+ m_expire = attotime::never;
+
+ return *this;
}
@@ -206,113 +236,103 @@ timer_instance &timer_instance::init_persistent(timer_callback &callback)
inline timer_instance &timer_instance::init_transient(timer_callback &callback, attotime const &duration)
{
- // transient timers are only saved on demand
+ assert(callback.persistent() == nullptr);
+ m_callback = &callback;
// ensure the entire timer state is clean
- m_start = callback.m_scheduler->time();
- m_expire = m_start + duration;
- m_period = attotime::never;
- m_callback = &callback;
- m_param = 0;
- m_param2 = 0;
- m_param3 = 0;
- m_id = 0;
- m_enabled = true;
- m_transient = true;
+ m_param[0] = m_param[1] = m_param[2] = 0;
+ m_active = false;
- // add to the active queue
- return callback.m_scheduler->insert_active(*this);
+ // add immediately to the active queue
+ attotime start = callback.scheduler().time();
+ return insert(start, start + duration);
}
//-------------------------------------------------
-// enable - enable/disable a timer
+// elapsed - return the amount of time since the
+// timer was started
//-------------------------------------------------
-bool timer_instance::enable(bool enable)
+attotime timer_instance::elapsed() const noexcept
{
- // reschedule only if the state has changed
- const bool old = m_enabled;
- if (old != enable)
- scheduler().instance_move(*this, m_expire, enable);
- return old;
+ return scheduler().time() - m_start;
}
//-------------------------------------------------
-// adjust - adjust the time when this timer will
-// fire and specify a period for subsequent
-// firings
+// remaining - return the amount of time
+// remaining until the timer expires
//-------------------------------------------------
-timer_instance &timer_instance::adjust(attotime const &start_delay, s32 param, attotime const &period)
+attotime timer_instance::remaining() const noexcept
{
- // set the start and expire times
- m_start = scheduler().time();
- m_period = period.is_zero() ? attotime::never : period;
- m_param = param;
-
- // move it into place
- if (start_delay.seconds() >= 0)
- scheduler().instance_move(*this, m_start + start_delay, true);
- else
- scheduler().instance_move(*this, m_start, true);
- return *this;
+ attotime curtime = scheduler().time();
+ if (curtime >= m_expire)
+ return attotime::zero;
+ return m_expire - curtime;
}
//-------------------------------------------------
-// elapsed - return the amount of time since the
-// timer was started
+// save - save our state to the given save data
+// structure
//-------------------------------------------------
-attotime timer_instance::elapsed() const noexcept
+timer_instance &timer_instance::save(timer_instance_save &dst)
{
- return scheduler().time() - m_start;
+ dst.start = m_start;
+ dst.expire = m_expire;
+ dst.param[0] = m_param[0];
+ dst.param[1] = m_param[1];
+ dst.param[2] = m_param[2];
+ dst.hash = m_callback->unique_hash();
+ dst.save_index = m_callback->save_index();
+ return *this;
}
//-------------------------------------------------
-// remaining - return the amount of time
-// remaining until the timer expires
+// restore - restore our state from the given
+// save data structure
//-------------------------------------------------
-attotime timer_instance::remaining() const noexcept
+timer_instance &timer_instance::restore(timer_instance_save const &src, timer_callback &callback, bool enabled)
{
- attotime curtime = scheduler().time();
- if (curtime >= m_expire)
- return attotime::zero;
- return m_expire - curtime;
+ m_callback = &callback;
+ m_param[0] = src.param[0];
+ m_param[1] = src.param[1];
+ m_param[2] = src.param[2];
+ m_active = false;
+ m_start = src.start;
+ m_expire = src.expire;
+ return enabled ? insert(src.start, src.expire) : *this;
}
//-------------------------------------------------
-// register_save - register ourself with the save
-// state system
+// insert - insert us into the scheduler's
+// active timer queue
//-------------------------------------------------
-void timer_instance::register_save()
+timer_instance &timer_instance::insert(attotime const &start, attotime const &expire)
{
- u32 this_hash = m_callback->m_unique_hash;
- char const *this_name = m_callback->m_unique_id.c_str();
+ m_start = start;
+ m_expire = expire;
+ m_active = true;
+ return m_callback->scheduler().insert_instance(*this);
+}
- int index = 0;
- for (timer_instance *curtimer = scheduler().m_active_timers.head(); curtimer != nullptr; curtimer = curtimer->next())
- if (!curtimer->m_transient && curtimer->m_callback->m_unique_hash == this_hash)
- index++;
- for (timer_instance *curtimer = scheduler().m_inactive_timers.head(); curtimer != nullptr; curtimer = curtimer->next())
- if (!curtimer->m_transient && curtimer->m_callback->m_unique_hash == this_hash)
- index++;
- // save the bits
- device_t *this_device = (m_callback != nullptr) ? m_callback->device() : nullptr;
- machine().save().save_item(this_device, "timer", this_name, index, NAME(m_start));
- machine().save().save_item(this_device, "timer", this_name, index, NAME(m_expire));
- machine().save().save_item(this_device, "timer", this_name, index, NAME(m_period));
- machine().save().save_item(this_device, "timer", this_name, index, NAME(m_param));
- machine().save().save_item(this_device, "timer", this_name, index, NAME(m_param2));
- machine().save().save_item(this_device, "timer", this_name, index, NAME(m_param3));
- machine().save().save_item(this_device, "timer", this_name, index, NAME(m_enabled));
+//-------------------------------------------------
+// remove - remove us from the scheduler's
+// active timer queue
+//-------------------------------------------------
+
+timer_instance &timer_instance::remove()
+{
+ m_active = false;
+ return m_callback->scheduler().remove_instance(*this);
}
@@ -323,15 +343,24 @@ void timer_instance::register_save()
void timer_instance::dump() const
{
- machine().logerror("%p: en=%d temp=%d exp=%15s start=%15s per=%15s param=%d/%lld/%lld ptr=%p",
- this, m_enabled, m_transient, m_expire.as_string(PRECISION), m_start.as_string(PRECISION), m_period.as_string(PRECISION), m_param, m_param2, m_param3, ptr());
- if (m_callback == nullptr || m_callback->device() == nullptr)
- if (m_callback->name() == nullptr)
- machine().logerror(" cb=NULL\n");
- else
- machine().logerror(" cb=%s\n", m_callback->name());
+ persistent_timer *persistent = m_callback->persistent();
+
+ running_machine &machine = scheduler().machine();
+ machine.logerror("%p: %s exp=%15s start=%15s ptr=%p param=%lld/%lld/%lld",
+ this,
+ (m_callback->persistent() != nullptr) ? "P" : "T",
+ m_expire.as_string(PRECISION),
+ m_start.as_string(PRECISION),
+ m_callback->ptr(),
+ m_param[0], m_param[1], m_param[2]);
+
+ if (persistent != nullptr)
+ machine.logerror(" per=%15s", persistent->period().as_string(PRECISION));
+
+ if (m_callback->device() != nullptr)
+ machine.logerror(" dev=%s id=%d\n", m_callback->device()->tag(), int(param(2)));
else
- machine().logerror(" dev=%s id=%d\n", m_callback->device()->tag(), id());
+ machine.logerror(" cb=%s\n", m_callback->name());
}
@@ -344,7 +373,10 @@ void timer_instance::dump() const
// persistent_timer - constructor
//-------------------------------------------------
-persistent_timer::persistent_timer()
+persistent_timer::persistent_timer() :
+ m_modified(0),
+ m_callback(this),
+ m_periodic_callback(this)
{
}
@@ -359,52 +391,154 @@ persistent_timer::~persistent_timer()
//-------------------------------------------------
-// init - initialize a device persistent timer
+// enable - enable a timer, returning the
+// previous state
//-------------------------------------------------
-persistent_timer &persistent_timer::init(device_t &device, device_timer_id id)
+bool persistent_timer::enable(bool enable)
{
- m_callback = device.device_timer_factory().callback();
- m_instance.init_persistent(m_callback).set_id(id);
+ // fetch the previous state and set the new one
+ bool old = enabled();
+ m_enabled = enable;
+
+ // if nothing changed, leave it alone
+ if (old == enable)
+ return old;
+
+ // remove if previously active
+ if (m_instance.active())
+ m_instance.remove();
+
+ // only re-insert if enabled
+ if (enable)
+ m_instance.insert(m_instance.start(), m_instance.expire());
+
+ // mark as modified
+ m_modified = true;
+ return old;
+}
+
+
+//-------------------------------------------------
+// adjust - change the timer's start time,
+// parameter, or period
+//-------------------------------------------------
+
+persistent_timer &persistent_timer::adjust(attotime const &start_delay, s32 param, attotime const &period)
+{
+ // set the parameters first
+ m_instance.set_param(param);
+
+ // adjust implicitly enables the timer
+ m_enabled = true;
+
+ // set the period and adjust the callback appropriately
+ m_period = period.is_zero() ? attotime::never : period;
+ if (periodic())
+ m_instance.m_callback = &m_periodic_callback;
+ else
+ m_instance.m_callback = &m_callback;
+
+ // compute start/expire times
+ attotime start = m_callback.scheduler().time();
+ attotime expire = start;
+ if (start_delay.seconds() >= 0)
+ expire += start_delay;
+
+ // then insert into the active list, removing first if previously active
+ if (m_instance.active())
+ m_instance.remove();
+ m_instance.insert(start, expire);
+
+ // mark as modified
+ m_modified = true;
return *this;
}
+//-------------------------------------------------
+// init_common - handle common initialization
+// tasks
+//-------------------------------------------------
+
+persistent_timer &persistent_timer::init_common(device_timer_id id)
+{
+ // initialize the timer instance
+ m_instance.init_persistent(m_callback);
+
+ // create the periodic callback
+ // FIX ME: Use the new C++ formatting stuff that I can't examples of find right now
+ char temp[10];
+ sprintf(temp, "%d", id);
+ m_periodic_callback.enregister(m_callback.scheduler(), *this, FUNC(persistent_timer::periodic_callback), temp);
+
+ return *this;
+}
-//**************************************************************************
-// TRANSIENT TIMER FACTORY
-//**************************************************************************
//-------------------------------------------------
-// transient_timer_factory - constructor
+// save - save persistent timer data to the given
+// save data structure
//-------------------------------------------------
-transient_timer_factory::transient_timer_factory()
+persistent_timer &persistent_timer::save(timer_instance_save &dst)
+{
+ m_instance.save(dst);
+
+ // overwrite the hash/save_index from the instance becuase it could be pointing
+ // to our periodic callback and we want the real underlying callback
+ dst.hash = m_callback.unique_hash();
+ dst.save_index = m_callback.save_index();
+ dst.period = m_period;
+ dst.enabled = enabled();
+ return *this;
+}
+
+
+//-------------------------------------------------
+// restore - restore persistent timer data from
+// the given save data structure
+//-------------------------------------------------
+
+persistent_timer &persistent_timer::restore(timer_instance_save const &src, timer_callback &callback)
{
+ m_period = src.period;
+ m_enabled = src.enabled;
+ m_instance.restore(src, periodic() ? m_periodic_callback : m_callback, m_enabled);
+ return *this;
}
//-------------------------------------------------
-// call_after - create a new timer that will
-// call the callback after a given amount of time
+// periodic_callback - callback to handle
+// periodic timers
//-------------------------------------------------
-void transient_timer_factory::call_after(attotime const &duration, u64 param, u64 param2, u64 param3)
+void persistent_timer::periodic_callback(timer_instance const &timer)
{
- m_callback.scheduler().instance_alloc().init_transient(m_callback, duration)
- .set_param(param).set_param2(param2).set_param3(param3);
+ // clear the modified state
+ m_modified = false;
+
+ // call the real callback
+ m_callback(timer);
+
+ // if the timer wasn't modified during the callback, advance by one period
+ if (!m_modified)
+ m_instance.insert(m_instance.m_expire, m_instance.m_expire + m_period);
}
+
+//**************************************************************************
+// TRANSIENT TIMER FACTORY
+//**************************************************************************
+
//-------------------------------------------------
-// call_after - create a new timer that will
-// call the callback after a given amount of time
+// transient_timer_factory - constructor
//-------------------------------------------------
-void transient_timer_factory::call_after(device_timer_id id, attotime const &duration, u64 param, u64 param2, u64 param3)
+transient_timer_factory::transient_timer_factory()
{
- m_callback.scheduler().instance_alloc().init_transient(m_callback, duration)
- .set_id(id).set_param(param).set_param2(param2).set_param3(param3);
}
@@ -705,15 +839,26 @@ device_scheduler::device_scheduler(running_machine &machine) :
m_free_timers(nullptr),
m_registered_callbacks(nullptr),
m_callback_timer(nullptr),
- m_callback_timer_modified(false),
m_callback_timer_expire_time(attotime::zero),
m_suspend_changes_pending(true),
m_quantum_minimum(ATTOSECONDS_IN_NSEC(1) / 1000)
{
// register global states
- machine.save().save_item(NAME(m_basetime));
- machine.save().register_presave(save_prepost_delegate(FUNC(device_scheduler::presave), this));
- machine.save().register_postload(save_prepost_delegate(FUNC(device_scheduler::postload), this));
+ auto &save = machine.save();
+ save.save_item(NAME(m_basetime));
+
+ // we could use STRUCT_MEMBER here if it worked on attotimes, but it doesn't
+ // currently, so do it the manual way
+ for (int index = 0; index < MAX_SAVE_INSTANCES; index++)
+ {
+ save.save_item(NAME(m_timer_save[index].start), index);
+ save.save_item(NAME(m_timer_save[index].expire), index);
+ save.save_item(NAME(m_timer_save[index].param), index);
+ save.save_item(NAME(m_timer_save[index].hash), index);
+ save.save_item(NAME(m_timer_save[index].save_index), index);
+ save.save_item(NAME(m_timer_save[index].enabled), index);
+ save.save_item(NAME(m_timer_save[index].period), index);
+ }
m_empty_timer.init(*this, *this, FUNC(device_scheduler::empty_timer));
m_timed_trigger.init(*this, *this, FUNC(device_scheduler::timed_trigger));
@@ -755,7 +900,7 @@ bool device_scheduler::can_save() const
// if any live transient timers exist, fail (transient timers are
// always active, so only need to scan the active list)
for (timer_instance *timer = m_active_timers.head(); timer != nullptr; timer = timer->next())
- if (timer->m_transient && !timer->expire().is_never())
+ if (timer->m_callback->persistent() == nullptr)
{
machine().logerror("Failed save state attempt due to anonymous timers:\n");
dump_timers();
@@ -997,10 +1142,18 @@ void device_scheduler::boost_interleave(attotime const &timeslice_time, attotime
// expired callback
//-------------------------------------------------
-void device_scheduler::register_callback(timer_callback &callback)
+u32 device_scheduler::register_callback(timer_callback &callback)
{
- callback.m_next = m_registered_callbacks;
+ // look for duplicates and compute a unique id
+ u32 index = 0;
+ for (timer_callback *scan = m_registered_callbacks; scan != nullptr; scan = scan->m_next_registered)
+ if (scan->unique_hash() == callback.unique_hash())
+ index++;
+
+ // now hook us in
+ callback.m_next_registered = m_registered_callbacks;
m_registered_callbacks = &callback;
+ return index;
}
@@ -1011,10 +1164,10 @@ void device_scheduler::register_callback(timer_callback &callback)
void device_scheduler::deregister_callback(timer_callback &callback)
{
- for (timer_callback **nextptr = &m_registered_callbacks; *nextptr != nullptr; nextptr = &(*nextptr)->m_next)
+ for (timer_callback **nextptr = &m_registered_callbacks; *nextptr != nullptr; nextptr = &(*nextptr)->m_next_registered)
if (*nextptr == &callback)
{
- *nextptr = callback.m_next;
+ *nextptr = callback.m_next_registered;
return;
}
}
@@ -1044,11 +1197,11 @@ persistent_timer *device_scheduler::timer_alloc(timer_expired_delegate const &ca
persistent_timer *device_scheduler::timer_alloc(device_t &device, device_timer_id id, void *ptr)
{
// allocate a new persistent timer and save it in a vector
- m_allocated_persistents.push_back(std::make_unique<persistent_timer>());
- persistent_timer &timer = *m_allocated_persistents.back().get();
+ m_allocated_persistents.push_back(std::make_unique<device_persistent_timer>());
+ device_persistent_timer &timer = static_cast<device_persistent_timer &>(*m_allocated_persistents.back().get());
// initialize the timer instance
- return &timer.init(device, id).set_ptr(ptr);
+ return &timer.init(device, id, ptr);
}
@@ -1070,11 +1223,41 @@ void device_scheduler::eat_all_cycles()
void device_scheduler::presave()
{
- // report the timer state after a log
- LOG("Prior to saving state:\n");
+ int index = 0;
+
#if VERBOSE
dump_timers();
#endif
+
+ // copy in all the timer instance data to the save area
+ for (timer_instance *timer = m_active_timers.head(); timer != nullptr && index < MAX_SAVE_INSTANCES; timer = timer->next())
+ {
+ auto *persistent = timer->m_callback->persistent();
+ if (persistent != nullptr)
+ persistent->save(m_timer_save[index++]);
+ else
+ timer->save(m_timer_save[index++]);
+ }
+
+ // then copy in inactive persistent timers
+ for (timer_callback *cb = m_registered_callbacks; cb != nullptr && index < MAX_SAVE_INSTANCES; cb = cb->m_next_registered)
+ if (cb->persistent() != nullptr && !cb->persistent()->instance().active())
+ cb->persistent()->save(m_timer_save[index++]);
+
+ // zero out the remainder
+ for ( ; index < MAX_SAVE_INSTANCES; index++)
+ {
+ auto &dest = m_timer_save[index];
+ dest.start = attotime::zero;
+ dest.expire = attotime::never;
+ dest.period = attotime::never;
+ dest.param[0] = dest.param[1] = dest.param[2] = 0;
+ dest.hash = 0;
+ dest.enabled = false;
+ }
+
+ // report the timer state after a log
+ LOG("Prior to saving state:\n");
}
@@ -1084,39 +1267,39 @@ void device_scheduler::presave()
void device_scheduler::postload()
{
- // upon reloading, all the timers' parameters and expiration times
- // will be different; because we rely on this information to tell us
- // which category (active/inactive) they belong to, we have to be
- // careful in how we maniuplate the timers
-
- // our approach here is to remove all the timers in each list directly,
- // discarding any transient timers, and appending persistent ones to
- // a local list; once all timers have been rescued this way, we
- // reassemble the final list
-
- timer_list private_list;
- timer_instance *timer;
-
// first discard or capture active timers
+ timer_instance *timer;
while ((timer = m_active_timers.remove_head()) != nullptr)
- if (timer->m_transient)
- instance_reclaim(*timer);
- else
- private_list.insert_tail(*timer);
+ instance_reclaim(*timer);
- // then discard or capture inactive timers
- while ((timer = m_inactive_timers.remove_head()) != nullptr)
- if (timer->m_transient)
- instance_reclaim(*timer);
- else
- private_list.insert_tail(*timer);
+ // now go through the restored save area and recreate all the timers
+ for (int index = 0; index < MAX_SAVE_INSTANCES; index++)
+ {
+ // scan until we find a never-expiring timer
+ auto &dest = m_timer_save[index];
+ if (dest.expire.is_never())
+ break;
+
+ // first find a matching callback
+ timer_callback *cb;
+ for (cb = m_registered_callbacks; cb != nullptr; cb = cb->m_next_registered)
+ if (cb->unique_hash() == dest.hash && cb->save_index() == dest.save_index)
+ break;
+
+ // if we can't find the timer, that's a concern (probably fatal)
+ if (cb == nullptr)
+ {
+ osd_printf_warning("Unable to find matching callback for %08X\n", dest.hash);
+ continue;
+ }
- // now re-insert them; this effectively re-sorts them by time
- while ((timer = private_list.remove_head()) != nullptr)
- if (timer->active())
- m_active_timers.insert_sorted(*timer);
+ // if the callback is persistent, just configure the persistent timer
+ auto *persistent = cb->persistent();
+ if (persistent != nullptr)
+ persistent->restore(dest, *cb);
else
- m_inactive_timers.insert_tail(*timer);
+ instance_alloc().restore(dest, *cb);
+ }
// force a refresh of things that are lazily updated
update_first_timer_expire();
@@ -1144,64 +1327,26 @@ inline void device_scheduler::execute_timers()
{
// if this is a one-shot timer, disable it now
timer_instance &timer = *m_active_timers.remove_head();
+ timer.m_active = false;
- // handle transient timers specially
- if (timer.m_transient)
- {
- // set the global state of which callback we're in
- m_callback_timer = &timer;
- m_callback_timer_expire_time = timer.m_expire;
-
- // call the callback
- g_profiler.start(PROFILER_TIMER_CALLBACK);
- {
- if (timer.m_callback->device() != nullptr)
- LOG("execute_timers: timer device %s timer %d\n", timer.m_callback->device()->tag(), timer.id());
- else
- LOG("execute_timers: timer callback %s\n", timer.m_callback->name());
+ // set the global state of which callback we're in
+ m_callback_timer = &timer;
+ m_callback_timer_expire_time = timer.m_expire;
- timer.m_callback->m_callback(timer);
- }
- g_profiler.stop();
-
- // reclaim the timer now that we're done with it
- instance_reclaim(timer);
- }
- else
+ // call the callback
+ g_profiler.start(PROFILER_TIMER_CALLBACK);
{
- // if not repeating, mark the timer disabled now
- if (timer.m_period.is_never())
- timer.m_enabled = 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.m_expire;
-
- // call the callback
- g_profiler.start(PROFILER_TIMER_CALLBACK);
- {
- if (timer.m_callback->device() != nullptr)
- LOG("execute_timers: timer device %s timer %d\n", timer.m_callback->device()->tag(), timer.id());
- else
- LOG("execute_timers: timer callback %s\n", timer.m_callback->name());
- timer.m_callback->m_callback(timer);
- }
- g_profiler.stop();
-
- // if the timer wasn't modified during the callback, advance by one period
- if (!m_callback_timer_modified)
- {
- timer.m_start = timer.m_expire;
- timer.m_expire += timer.m_period;
- }
-
- // insert into the appropriate list
- if (timer.active())
- m_active_timers.insert_sorted(timer);
+ if (timer.m_callback->device() != nullptr)
+ LOG("execute_timers: timer device %s timer %d\n", timer.m_callback->device()->tag(), int(timer.param(2)));
else
- m_inactive_timers.insert_tail(timer);
+ LOG("execute_timers: timer callback %s\n", timer.m_callback->name());
+
+ (*timer.m_callback)(timer);
}
+ g_profiler.stop();
+
+ // reclaim the timer now that we're done with it
+ instance_reclaim(timer);
}
// update the expiration time of the first timer
@@ -1413,36 +1558,6 @@ void device_scheduler::add_scheduling_quantum(attotime const &quantum, attotime
//-------------------------------------------------
-// insert_active - insert a timer instance at
-// the the appropriate spot in the active
-// timer queue
-//-------------------------------------------------
-
-inline timer_instance &device_scheduler::insert_active(timer_instance &instance)
-{
- // if insert_sorted returns true, it means we were inserted at the
- // head, and need to recompute our next fire time
- if (m_active_timers.insert_sorted(instance))
- {
- update_first_timer_expire();
- abort_timeslice();
- }
- return instance;
-}
-
-
-//-------------------------------------------------
-// insert_inactive - insert a timer instance at
-// the end of the inactive queue
-//-------------------------------------------------
-
-inline timer_instance &device_scheduler::insert_inactive(timer_instance &instance)
-{
- return m_inactive_timers.insert_tail(instance);
-}
-
-
-//-------------------------------------------------
// instance_alloc - allocate memory for a new
// timer instance, either by reclaiming a
// freed one, or allocating memory for a new one
@@ -1471,6 +1586,11 @@ inline timer_instance &device_scheduler::instance_alloc()
inline void device_scheduler::instance_reclaim(timer_instance &timer)
{
+ // don't reclaim persistent instances because they are part of the
+ // persistent_timer object
+ if (timer.m_callback->persistent() != nullptr)
+ return;
+
// reclaimed instances go back on the free list
timer.m_next = m_free_timers;
m_free_timers = &timer;
@@ -1478,46 +1598,36 @@ inline void device_scheduler::instance_reclaim(timer_instance &timer)
//-------------------------------------------------
-// instance_move - move an existing timer into
-// the appropriate list at the appropriate
-// location
+// insert_instance - insert a timer instance at
+// the the appropriate spot in the active
+// timer queue
//-------------------------------------------------
-inline void device_scheduler::instance_move(timer_instance &timer, attotime const &new_expire, bool new_enable)
+inline timer_instance &device_scheduler::insert_instance(timer_instance &instance)
{
- // track the before/after active state to see if it changed
- bool was_active = timer.active();
- timer.m_expire = new_expire;
- timer.m_enabled = new_enable;
-
- // if this is the active timer, don't move anything, just mark it modified
- if (m_callback_timer == &timer)
+ // if insert_sorted returns true, it means we were inserted at the
+ // head, and need to recompute our next fire time
+ if (m_active_timers.insert_sorted(instance))
{
- m_callback_timer_modified = true;
- return;
+ update_first_timer_expire();
+ abort_timeslice();
}
+ return instance;
+}
- // most common case is becoming/remaining active
- if (timer.active())
- {
- // ok, we're active now; remove us from the previous list, then do
- // a normal insert to the active list
- (was_active ? m_active_timers : m_inactive_timers).remove(timer);
- insert_active(timer);
- }
- else if (was_active)
- {
- bool was_first = (timer.m_prev == nullptr);
- // remove and then insert into the inactive list
- insert_inactive(m_active_timers.remove(timer));
+//-------------------------------------------------
+// remove_instance - remove a timer from the
+// active timer queue
+//-------------------------------------------------
- // if we were previously the head, update the first timer expiration;
- // no need to abort the current timeslice because the new expiration
- // time will be later than before
- if (was_first)
- update_first_timer_expire();
- }
+inline timer_instance &device_scheduler::remove_instance(timer_instance &instance)
+{
+ bool was_first = (instance.prev() == nullptr);
+ m_active_timers.remove(instance);
+ if (was_first)
+ update_first_timer_expire();
+ return instance;
}
@@ -1552,7 +1662,5 @@ void device_scheduler::dump_timers() const
machine().logerror("Timer Dump: Time = %15s\n", time().as_string(PRECISION));
for (timer_instance *timer = m_active_timers.head(); timer != nullptr; timer = timer->next())
timer->dump();
- for (timer_instance *timer = m_inactive_timers.head(); timer != nullptr; timer = timer->next())
- timer->dump();
machine().logerror("=============================================\n");
}
diff --git a/src/emu/schedule.h b/src/emu/schedule.h
index 9334240ab4a..d17e573c565 100644
--- a/src/emu/schedule.h
+++ b/src/emu/schedule.h
@@ -29,21 +29,42 @@
// TYPE DEFINITIONS
//**************************************************************************
+// forward definitions
+class persistent_timer;
+
// timer IDs for devices
using device_timer_id = u32;
-// timer callbacks look like this
+// timer callbacks look like this natively
using timer_expired_delegate_native = named_delegate<void (timer_instance const &)>;
+
+// alternate form #1 takes no parameters
using timer_expired_delegate_form1 = named_delegate<void ()>;
+
+// alternate form #2 matches the device_timer callback used by devices
using timer_expired_delegate_form2 = named_delegate<void (timer_instance const &, device_timer_id, int, void *)>;
-template<typename IntType> using timer_expired_delegate_form3 = named_delegate<void (IntType)>;
-template<typename IntType> using timer_expired_delegate_form4 = named_delegate<void (void *, IntType)>;
-template<typename IntType, typename IntType2> using timer_expired_delegate_form5 = named_delegate<void (IntType, IntType2)>;
-template<typename IntType, typename IntType2, typename IntType3> using timer_expired_delegate_form6 = named_delegate<void (IntType, IntType2, IntType3)>;
+
+// alternate form #3 takes a single integer parameter of any type
+template<typename IntType>
+using timer_expired_delegate_form3 = named_delegate<void (IntType)>;
+
+// alternate form #4 takes a pointer value and an integer parameter; this is the classic TIMER_CALLBACK
+template<typename IntType>
+using timer_expired_delegate_form4 = named_delegate<void (void *, IntType)>;
+
+// alternate form #5 takes two integer parameters of any type; maps to some write handlers
+template<typename IntType, typename IntType2>
+using timer_expired_delegate_form5 = named_delegate<void (IntType, IntType2)>;
+
+// alternate form #6 takes three integer parameters of any type; maps to some write handlers
+template<typename IntType, typename IntType2, typename IntType3>
+using timer_expired_delegate_form6 = named_delegate<void (IntType, IntType2, IntType3)>;
// ======================> timer_expired_delegate
+// a timer_expired_delegate represents a bound timer expired callback; it can wrap
+// all of the above alternate forms via built-in trampolines
class timer_expired_delegate : public timer_expired_delegate_native
{
// this is just a substitute for an arbitrary delegate; it presumes that
@@ -67,6 +88,7 @@ public:
// copy assignment
timer_expired_delegate &operator=(timer_expired_delegate const &src)
{
+ // copy the native and sub delegates
*static_cast<timer_expired_delegate_native *>(this) = src;
m_sub_delegate = src.m_sub_delegate;
@@ -101,7 +123,7 @@ public:
reinterpret_cast<timer_expired_delegate_form1 &>(m_sub_delegate) = timer_expired_delegate_form1(cb, name, bindto);
}
- // form 2 constructor: void timer_callback(timer_instance const &timer, device_timer_id id, int param, void *ptr) -- device_timer style
+ // form 2 constructor: void timer_callback(timer_instance const &timer, device_timer_id id, int param, void *ptr)
template<typename FuncDeviceType, typename DeviceType>
timer_expired_delegate(void (FuncDeviceType::*cb)(timer_instance const &, device_timer_id, int, void *), char const *name, DeviceType *bindto) :
timer_expired_delegate_native(FUNC(timer_expired_delegate::form2_callback), this)
@@ -119,7 +141,7 @@ public:
reinterpret_cast<timer_expired_delegate_form3<IntType> &>(m_sub_delegate) = timer_expired_delegate_form3<IntType>(cb, name, bindto);
}
- // form 4 constructor: void timer_callback(void *ptr, int param) -- legacy style
+ // form 4 constructor: void timer_callback(void *ptr, int param)
template<typename FuncDeviceType, typename DeviceType, typename IntType, std::enable_if_t<std::is_integral<IntType>::value, bool> = true>
timer_expired_delegate(void (FuncDeviceType::*cb)(void *ptr, IntType), char const *name, DeviceType *bindto) :
timer_expired_delegate_native(FUNC(timer_expired_delegate::form4_callback<IntType>), this)
@@ -171,6 +193,9 @@ private:
// ======================> timer_callback
+// a timer_callback represents a registered callback, along with a user-supplied
+// pointer and other useful information; timer_callbacks are used internally by
+// both the persistent_timer and transitent_timer_factory classes
class timer_callback
{
friend class device_scheduler;
@@ -179,7 +204,7 @@ class timer_callback
public:
// construction/destruction
- timer_callback();
+ timer_callback(persistent_timer *persistent = nullptr);
~timer_callback();
// copy constructor
@@ -188,11 +213,11 @@ public:
// copy assignment
timer_callback &operator=(timer_callback const &src);
- // registration of a delegate directly
- timer_callback &enregister_base(device_scheduler &scheduler, timer_expired_delegate const &callback, char const *unique);
+ // calling operator
+ void operator()(timer_instance const &timer) { m_delegate(timer); }
- // registration of a delegate directly, adding device tag to unique id
- timer_callback &enregister_device(device_t &device, timer_expired_delegate const &callback, char const *unique);
+ // registration of a delegate directly
+ timer_callback &enregister_base(device_scheduler &scheduler, timer_expired_delegate const &callback, char const *unique, char const *unique2 = nullptr);
// registration of an arbitrary member function bound to an arbitrary object; requires the
// device_scheduler as the first parameter since we don't know how to get one
@@ -210,6 +235,7 @@ public:
}
// registration of a device interface member function bound to the interface
+ // this is only enabled if the call is NOT a device_t (to prevent ambiguity)
template<typename IntfType, typename FuncType, std::enable_if_t<std::is_base_of<device_interface, IntfType>::value && !std::is_base_of<device_t, IntfType>::value, bool> = true>
timer_callback &enregister(IntfType &intf, FuncType callback, char const *string, char const *unique = nullptr)
{
@@ -218,28 +244,58 @@ public:
// getters
device_scheduler &scheduler() const { assert(m_scheduler != nullptr); return *m_scheduler; }
- char const *name() const { return m_callback.name(); }
+ char const *name() const { return m_delegate.name(); }
void *ptr() const { return m_ptr; }
device_t *device() const { return m_device; }
+ persistent_timer *persistent() const { return m_persistent; }
+ u32 unique_hash() const { return m_unique_hash; }
+ u32 save_index() const { return m_save_index; }
+ char const *unique_id() const { return m_unique_id.c_str(); }
// setters
- timer_callback &set_ptr(void *ptr) { m_ptr = ptr; return *this; }
- timer_callback &set_device(device_t &device) { m_device = &device; return *this; }
+ timer_callback &set_ptr(void *ptr);
+ timer_callback &set_device(device_t &device);
private:
+ // registration of a delegate directly, adding device tag to unique id
+ timer_callback &enregister_device(device_t &device, timer_expired_delegate const &callback, char const *unique);
+
// internal state
- timer_expired_delegate m_callback; // the full delegate
- void *m_ptr; // user-supplied pointer
- device_t *m_device; // pointer to device, if there is one
- device_scheduler *m_scheduler; // pointer to the scheduler
- timer_callback *m_next; // link to the next registered item
- u32 m_unique_hash; // hash of the unique ID
- std::string m_unique_id; // a unique ID string
+ timer_expired_delegate m_delegate; // the full delegate
+ void *m_ptr; // user-supplied pointer
+ device_scheduler *m_scheduler; // pointer to the scheduler
+ timer_callback *m_next_registered; // link to the next registered item
+ persistent_timer *m_persistent; // pointer to our owning persistent timer, or nullptr
+ device_t *m_device; // pointer to device, for debugging/logging
+ u32 m_unique_hash; // hash of the unique ID
+ u32 m_save_index; // index for saving
+ std::string m_unique_id; // a unique ID string
+};
+
+
+// ======================> timer_instance_save
+
+// timer_instance_save is an internal structure that holds a single saved
+// timer instance, along with some persistent_timer data for instances that
+// are owned by a persistent_timer
+struct timer_instance_save
+{
+ attotime start; // saved/restore by timer_instance
+ attotime expire; // saved/restore by timer_instance
+ u64 param[3]; // saved/restore by timer_instance
+ u32 hash; // saved/restore by timer_instance/persistent_timer
+ u16 save_index; // saved/restore by persistent_timer
+ u8 enabled; // saved/restore by persistent_timer
+ attotime period; // saved/restore by persistent_timer
};
// ======================> timer_instance
+// a timer_instance represents an intantiated instance of a timer; for persistent
+// timers, there is one timer_instance embedded as part of the presistent_timer
+// object; for transient timers, timer_instances are allocated on the fly whenever
+// the transient_timer_factory is requested to issue a callback
class timer_instance
{
friend class device_scheduler;
@@ -247,49 +303,43 @@ class timer_instance
friend class transient_timer_factory;
friend class timer_list;
- // allocation and re-use
- timer_instance &init_transient(timer_callback &callback, attotime const &duration);
- timer_instance &init_persistent(timer_callback &callback);
+ DISABLE_COPYING(timer_instance);
public:
// construction/destruction
timer_instance();
~timer_instance();
+ // allocation and re-use
+ timer_instance &init_transient(timer_callback &callback, attotime const &duration);
+ timer_instance &init_persistent(timer_callback &callback);
+
// getters
- running_machine &machine() const noexcept;
- device_scheduler &scheduler() const noexcept;
+ device_scheduler &scheduler() const noexcept { return m_callback->scheduler(); }
timer_instance *prev() const { return m_prev; }
timer_instance *next() const { return m_next; }
- bool enabled() const { return m_enabled; }
- bool active() const { return m_enabled && !m_expire.is_never(); }
- u64 param() const { return m_param; }
- u64 param2() const { return m_param2; }
- u64 param3() const { return m_param3; }
- device_timer_id id() const { return m_id; }
+ u64 param(int index = 0) const { return m_param[index]; }
void *ptr() const { return m_callback->ptr(); }
+ bool active() const { return m_active; }
// setters
- bool enable(bool enable = true);
- timer_instance &set_param(u64 param) { m_param = param; return *this; }
- timer_instance &set_param2(u64 param2) { m_param2 = param2; return *this; }
- timer_instance &set_param3(u64 param3) { m_param3 = param3; return *this; }
- timer_instance &set_id(device_timer_id id) { m_id = id; return *this; }
-
- // control
- timer_instance &reset(attotime const &duration = attotime::never) { return adjust(duration, m_param, m_period); }
- timer_instance &adjust(attotime const &start_delay, s32 param = 0, attotime const &periodicity = attotime::never);
+ timer_instance &set_param(int index, u64 param) { m_param[index] = param; return *this; }
+ timer_instance &set_param(u64 param) { return set_param(0, param); }
+ timer_instance &set_params(u64 param0, u64 param1) { return set_param(0, param0).set_param(1, param1); }
+ timer_instance &set_params(u64 param0, u64 param1, u64 param2) { return set_param(0, param0).set_param(1, param1).set_param(2, param2); }
// timing queries
attotime elapsed() const noexcept;
attotime remaining() const noexcept;
attotime const &start() const { return m_start; }
attotime const &expire() const { return m_expire; }
- attotime const &period() const { return m_period; }
private:
// internal helpers
- void register_save();
+ timer_instance &save(timer_instance_save &dst);
+ timer_instance &restore(timer_instance_save const &src, timer_callback &callback, bool enabled = true);
+ timer_instance &insert(attotime const &start, attotime const &expire);
+ timer_instance &remove();
void dump() const;
// internal state
@@ -297,21 +347,22 @@ private:
timer_instance * m_prev; // previous timer in order in the list
attotime m_start; // time when the timer was started
attotime m_expire; // time when the timer will expire
- attotime m_period; // the repeat frequency of the timer
- timer_callback * m_callback; // pointer to an external callback
- u64 m_param; // integer parameter
- u64 m_param2; // larger integer parameter
- u64 m_param3; // larger integer parameter
- device_timer_id m_id; // for device timers, the ID of the timer
- bool m_enabled; // is the timer enabled?
- bool m_transient; // is this a transient timer?
+ timer_callback * m_callback; // pointer to the external callback
+ u64 m_param[3]; // integer parameters
+ bool m_active; // true if currently in the active list
};
// ======================> transient_timer_factory
+// a transient_timer_factory contains a timer_callback and can dynamically
+// create multiple timer_instances that call the callback after a certain
+// elapsed time; these timers are fire-and-forget, and it is not possible to
+// modify or cancel them once issued
class transient_timer_factory
{
+ DISABLE_COPYING(transient_timer_factory);
+
friend class timer_instance;
public:
@@ -320,98 +371,124 @@ public:
// initialization
template<typename... T>
- void init(T &&... args)
+ transient_timer_factory &init(T &&... args)
{
m_callback.enregister(std::forward<T>(args)...);
+ return *this;
}
+ // getters
+ timer_callback const &callback() const { return m_callback; }
+
// create a new timer_instance that will fire after the given duration
void call_after(attotime const &duration, u64 param = 0, u64 param2 = 0, u64 param3 = 0);
- // same as above, but accepting a timer ID up front as well
- void call_after(device_timer_id id, attotime const &duration, u64 param = 0, u64 param2 = 0, u64 param3 = 0);
-
// create a new timer_instance that will fire as soon as possible
void synchronize(u64 param = 0, u64 param2 = 0, u64 param3 = 0)
{
call_after(attotime::zero, param, param2, param3);
}
- // getters
- timer_callback const &callback() const { return m_callback; }
-
private:
// internal state
- timer_callback m_callback;
+ timer_callback m_callback; // the embedded callback
};
// ======================> persistent_timer
+// a persistent_timer contains a time_callback and a timer_instance, which
+// can be manipulated
class persistent_timer
{
+ friend class device_scheduler;
+
+ DISABLE_COPYING(persistent_timer);
+
public:
- // constructor
+ // construction/destruction
persistent_timer();
- ~persistent_timer();
+ virtual ~persistent_timer();
+
+#if 0
+ // initialization
+ template<typename... T>
+ persistent_timer &init(T &&... args)
+ {
+ m_callback.enregister(std::forward<T>(args)...);
+ return init_common();
+ }
- // generic initialization; matches any of the enregister forms; note that we test the
- // second parameter for int/enum/timer_expired_delegate to allow for the specialized
- // cases below
- template<typename T1, typename T2, typename... Tn, std::enable_if_t<!std::is_integral<T2>::value && !std::is_enum<T2>::value && !std::is_base_of<timer_expired_delegate, T2>::value, bool> = true>
+#else
+ // generic initialization; matches any of the enregister forms that take a member
+ // function pointer as the second parameter
+ template<typename T1, typename T2, typename... Tn, std::enable_if_t<std::is_member_function_pointer<T2>::value, bool> = true>
persistent_timer &init(T1 &&arg1, T2 &&arg2, Tn &&... args)
{
m_callback.enregister(std::forward<T1>(arg1), std::forward<T2>(arg2), std::forward<Tn>(args)...);
- m_instance.init_persistent(m_callback);
- return *this;
+ return init_common();
+ }
+
+ // similar to above, but for the case where we pass in the scheduler first
+ template<typename T1, typename T2, typename... Tn, std::enable_if_t<std::is_member_function_pointer<T2>::value, bool> = true>
+ persistent_timer &init(device_scheduler &scheduler, T1 &&arg1, T2 &&arg2, Tn &&... args)
+ {
+ m_callback.enregister(scheduler, std::forward<T1>(arg1), std::forward<T2>(arg2), std::forward<Tn>(args)...);
+ return init_common();
}
// initialize with the scheduler and a pre-formed timer_expired_delegate
persistent_timer &init(device_scheduler &scheduler, timer_expired_delegate const &callback, char const *unique = nullptr)
{
m_callback.enregister_base(scheduler, callback, unique);
- m_instance.init_persistent(m_callback);
- return *this;
+ return init_common();
}
-
- // initialize with a device and ID; implicitly calls the device's device_timer method
- persistent_timer &init(device_t &device, device_timer_id id);
+#endif
// getters
timer_instance const &instance() const { return m_instance; }
timer_callback const &callback() const { return m_callback; }
- bool enabled() const { return m_instance.enabled(); }
- bool active() const { return m_instance.active(); }
- u64 param() const { return m_instance.param(); }
- u64 param2() const { return m_instance.param2(); }
- u64 param3() const { return m_instance.param3(); }
+ u64 param(int index = 0) const { return m_instance.param(index); }
void *ptr() const { return m_callback.ptr(); }
- device_timer_id id() const { return m_instance.id(); }
+ bool enabled() const { return m_enabled && m_instance.active(); }
+ bool periodic() const { return !m_period.is_never(); }
+ attotime elapsed() const noexcept { return m_instance.elapsed(); }
+ attotime remaining() const noexcept { return m_instance.remaining(); }
+ attotime const &start() const { return m_instance.start(); }
+ attotime const &expire() const { return m_instance.expire(); }
+ attotime const &period() const { return m_period; }
// setters
- bool enable(bool newenable = true) { return m_instance.enable(newenable); }
- persistent_timer &set_param(u64 param) { m_instance.set_param(param); return *this; }
- persistent_timer &set_param2(u64 param2) { m_instance.set_param2(param2); return *this; }
- persistent_timer &set_param3(u64 param3) { m_instance.set_param3(param3); return *this; }
+ persistent_timer &set_param(int index, u64 param) { m_instance.set_param(index, param); return *this; }
+ persistent_timer &set_param(u64 param) { return set_param(0, param); }
+ persistent_timer &set_params(u64 param0, u64 param1) { return set_param(0, param0).set_param(1, param1); }
+ persistent_timer &set_params(u64 param0, u64 param1, u64 param2) { return set_param(0, param0).set_param(1, param1).set_param(2, param2); }
persistent_timer &set_ptr(void *ptr) { m_callback.set_ptr(ptr); return *this; }
// control
- persistent_timer &reset(attotime const &duration = attotime::never) { return adjust(duration, m_instance.param(), m_instance.period()); }
- persistent_timer &adjust(attotime const &start_delay, s32 param = 0, attotime const &periodicity = attotime::never) { m_instance.adjust(start_delay, param, periodicity); return *this; }
+ bool enable(bool enable = true);
+ bool disable() { return enable(false); }
+ persistent_timer &reset(attotime const &duration = attotime::never) { return adjust(duration, m_instance.param(), m_period); }
+ persistent_timer &adjust(attotime const &start_delay, s32 param = 0, attotime const &periodicity = attotime::never);
- // timing queries
- attotime elapsed() const noexcept { return m_instance.elapsed(); }
- attotime remaining() const noexcept { return m_instance.remaining(); }
- attotime const &start() const { return m_instance.start(); }
- attotime const &expire() const { return m_instance.expire(); }
- attotime const &period() const { return m_instance.period(); }
+protected:
+ // internal helpers
+ void periodic_callback(timer_instance const &timer);
+ persistent_timer &init_common(device_timer_id id = 0);
+ persistent_timer &save(timer_instance_save &dst);
+ persistent_timer &restore(timer_instance_save const &src, timer_callback &callback);
+ void register_save();
-private:
// internal state
- timer_instance m_instance;
- timer_callback m_callback;
+ attotime m_period; // the timer period, or attotime::never if not periodic
+ bool m_enabled; // true if enabled, false if disabled
+ bool m_modified; // true if modified
+ timer_instance m_instance; // the embedded timer instnace
+ timer_callback m_callback; // the embedded timer callback
+ timer_callback m_periodic_callback; // an wrapper callback for periodic timers
};
+// eventually replace emu_timer with persistent_timer
using emu_timer = persistent_timer;
@@ -421,11 +498,16 @@ class device_scheduler
{
friend class device_execute_interface;
friend class transient_timer_factory;
+ friend class persistent_timer;
friend class basetime_relative;
friend class timer_callback;
friend class timer_instance;
friend class device_t; // for access to timer_alloc/timer_set device forms
+ static constexpr int MAX_SAVE_INSTANCES = 256;
+
+ // inner private class for maintaining base-time relative values for
+ // faster comparisons vs a full attotime
class basetime_relative
{
public:
@@ -463,6 +545,8 @@ class device_scheduler
seconds_t m_base_seconds;
};
+ // simple doubly-linked list of items with embedded previous/next
+ // pointers, used for maintaining lists of timer instances
class timer_list
{
public:
@@ -503,7 +587,7 @@ public:
void suspend_resume_changed() { m_suspend_changes_pending = true; }
// timer callback registration
- void register_callback(timer_callback &callback);
+ u32 register_callback(timer_callback &callback);
void deregister_callback(timer_callback &callback);
// timers, specified by callback/name; using persistent_timer is preferred
@@ -540,13 +624,12 @@ private:
void add_scheduling_quantum(attotime const &quantum, attotime const &duration);
// helpers for other friends
- timer_instance &insert_active(timer_instance &instance);
- timer_instance &insert_inactive(timer_instance &instance);
+ timer_instance &insert_instance(timer_instance &instance);
+ timer_instance &remove_instance(timer_instance &instance);
// timer helpers
timer_instance &instance_alloc();
void instance_reclaim(timer_instance &timer);
- void instance_move(timer_instance &timer, attotime const &new_expire, bool new_enable);
// internal timers
void empty_timer(timer_instance const &timer);
@@ -564,9 +647,8 @@ private:
// list of active timers
basetime_relative m_first_timer_expire; // time of the first timer expiration
timer_list m_active_timers; // sorted list of active timers
- timer_list m_inactive_timers; // unsorted list of inactive timers
timer_instance * m_free_timers; // simple list of free timers
- timer_callback * m_registered_callbacks; // list of registered callbacks
+ timer_callback * m_registered_callbacks; // list of registered callbacks
transient_timer_factory m_empty_timer; // empty timer factory
transient_timer_factory m_timed_trigger; // timed trigger factory
std::vector<std::unique_ptr<persistent_timer>> m_allocated_persistents;
@@ -574,9 +656,9 @@ private:
// other internal states
timer_instance * m_callback_timer; // pointer to the current callback timer
- bool m_callback_timer_modified; // true if the current callback timer was modified
attotime m_callback_timer_expire_time; // the original expiration time
bool m_suspend_changes_pending; // suspend/resume changes are pending
+ timer_instance_save m_timer_save[MAX_SAVE_INSTANCES]; // state saving area
// scheduling quanta
class quantum_slot
@@ -598,18 +680,6 @@ private:
-inline running_machine &timer_instance::machine() const noexcept
-{
- return m_callback->m_scheduler->machine();
-}
-
-inline device_scheduler &timer_instance::scheduler() const noexcept
-{
- return *m_callback->m_scheduler;
-}
-
-
-
inline void timer_expired_delegate::form1_callback(timer_instance const &timer)
{
reinterpret_cast<timer_expired_delegate_form1 &>(m_sub_delegate)();
@@ -617,7 +687,7 @@ inline void timer_expired_delegate::form1_callback(timer_instance const &timer)
inline void timer_expired_delegate::form2_callback(timer_instance const &timer)
{
- reinterpret_cast<timer_expired_delegate_form2 &>(m_sub_delegate)(timer, timer.id(), timer.param(), timer.ptr());
+ reinterpret_cast<timer_expired_delegate_form2 &>(m_sub_delegate)(timer, device_timer_id(timer.param(2)), timer.param(), timer.ptr());
}
template<typename IntType>
@@ -645,4 +715,18 @@ inline void timer_expired_delegate::form6_callback(timer_instance const &timer)
}
+//-------------------------------------------------
+// call_after - create a new timer that will
+// call the callback after a given amount of time
+//-------------------------------------------------
+
+inline void transient_timer_factory::call_after(attotime const &duration, u64 param, u64 param2, u64 param3)
+{
+ assert(!duration.is_never());
+ m_callback.scheduler().instance_alloc().init_transient(m_callback, duration)
+ .set_params(param, param2, param3);
+}
+
+
+
#endif // MAME_EMU_SCHEDULE_H
diff --git a/src/emu/screen.h b/src/emu/screen.h
index af8b1af09b6..b9ae08138aa 100644
--- a/src/emu/screen.h
+++ b/src/emu/screen.h
@@ -511,8 +511,8 @@ private:
attotime m_vblank_end_time; // time of last VBLANK end
persistent_timer m_vblank_begin_timer; // timer to signal VBLANK start
persistent_timer m_vblank_end_timer; // timer to signal VBLANK end
- persistent_timer m_scanline0_timer; // scanline 0 timer
- persistent_timer m_scanline_timer; // scanline timer
+ device_persistent_timer m_scanline0_timer; // scanline 0 timer
+ device_persistent_timer m_scanline_timer; // scanline timer
u64 m_frame_number; // the current frame number
u32 m_partial_updates_this_frame;// partial update counter this frame