diff options
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 |
commit | 92e9d1e8928a0fbe7ccfb9f053b2434e2dae9d9c (patch) | |
tree | e2a7dc431aaf411ff7df3129b94254ece609871d | |
parent | 3a6239dd46f846e83604f25acc37bc8c83a2f034 (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.cpp | 2 | ||||
-rw-r--r-- | src/emu/device.h | 41 | ||||
-rw-r--r-- | src/emu/emu.h | 2 | ||||
-rw-r--r-- | src/emu/schedule.cpp | 694 | ||||
-rw-r--r-- | src/emu/schedule.h | 312 | ||||
-rw-r--r-- | src/emu/screen.h | 4 |
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 ×lice_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 |