// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** timer.cpp Timer devices. ***************************************************************************/ #include "emu.h" #include "screen.h" /*************************************************************************** // DEBUGGING ***************************************************************************/ #define VERBOSE 0 #define LOG(x) do { if (VERBOSE) logerror x; } while (0) //************************************************************************** // LIVE TIMER DEVICE //************************************************************************** const device_type TIMER = device_creator; //------------------------------------------------- // timer_device - constructor //------------------------------------------------- timer_device::timer_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, TIMER, "Timer", tag, owner, clock, "timer", __FILE__), m_type(TIMER_TYPE_GENERIC), m_callback(timer_device_expired_delegate()), m_ptr(nullptr), m_start_delay(attotime::zero), m_period(attotime::zero), m_param(0), m_screen_tag(nullptr), m_screen(nullptr), m_first_vpos(0), m_increment(0), m_timer(nullptr), m_first_time(true) { } //------------------------------------------------- // static_configure_generic - configuration // helper to set up a generic timer //------------------------------------------------- void timer_device::static_configure_generic(device_t &device, timer_device_expired_delegate callback) { timer_device &timer = downcast(device); timer.m_type = TIMER_TYPE_GENERIC; timer.m_callback = callback; } //------------------------------------------------- // static_configure_periodic - configuration // helper to set up a periodic timer //------------------------------------------------- void timer_device::static_configure_periodic(device_t &device, timer_device_expired_delegate callback, const attotime &period) { timer_device &timer = downcast(device); timer.m_type = TIMER_TYPE_PERIODIC; timer.m_callback = callback; timer.m_period = period; } //------------------------------------------------- // static_configure_scanline - configuration // helper to set up a scanline timer //------------------------------------------------- void timer_device::static_configure_scanline(device_t &device, timer_device_expired_delegate callback, const char *screen, int first_vpos, int increment) { timer_device &timer = downcast(device); timer.m_type = TIMER_TYPE_SCANLINE; timer.m_callback = callback; timer.m_screen_tag = screen; timer.m_first_vpos = first_vpos; timer.m_increment = increment; } //------------------------------------------------- // static_set_callback - configuration helper // to set the callback //------------------------------------------------- void timer_device::static_set_callback(device_t &device, timer_device_expired_delegate callback) { timer_device &timer = downcast(device); timer.m_callback = callback; } //------------------------------------------------- // static_set_start_delay - configuration helper // to set the starting delay //------------------------------------------------- void timer_device::static_set_start_delay(device_t &device, const attotime &delay) { timer_device &timer = downcast(device); timer.m_start_delay = delay; } //------------------------------------------------- // static_set_param - configuration helper to set // the integer parameter //------------------------------------------------- void timer_device::static_set_param(device_t &device, int param) { timer_device &timer = downcast(device); timer.m_param = param; } //------------------------------------------------- // static_set_ptr - configuration helper to set // the pointer parameter //------------------------------------------------- void timer_device::static_set_ptr(device_t &device, void *ptr) { timer_device &timer = downcast(device); timer.m_ptr = ptr; } //------------------------------------------------- // device_validity_check - validate the device // configuration //------------------------------------------------- void timer_device::device_validity_check(validity_checker &valid) const { // type based configuration switch (m_type) { case TIMER_TYPE_GENERIC: if (m_screen_tag != nullptr || m_first_vpos != 0 || m_start_delay != attotime::zero) osd_printf_warning("Generic timer specified parameters for a scanline timer\n"); if (m_period != attotime::zero || m_start_delay != attotime::zero) osd_printf_warning("Generic timer specified parameters for a periodic timer\n"); break; case TIMER_TYPE_PERIODIC: if (m_screen_tag != nullptr || m_first_vpos != 0) osd_printf_warning("Periodic timer specified parameters for a scanline timer\n"); if (m_period <= attotime::zero) osd_printf_error("Periodic timer specified invalid period\n"); break; case TIMER_TYPE_SCANLINE: if (m_period != attotime::zero || m_start_delay != attotime::zero) osd_printf_warning("Scanline timer specified parameters for a periodic timer\n"); if (m_param != 0) osd_printf_warning("Scanline timer specified parameter which is ignored\n"); // if (m_first_vpos < 0) // osd_printf_error("Scanline timer specified invalid initial position\n"); // if (m_increment < 0) // osd_printf_error("Scanline timer specified invalid increment\n"); break; default: osd_printf_error("Invalid type specified\n"); break; } } //------------------------------------------------- // device_start - perform device-specific // startup //------------------------------------------------- void timer_device::device_start() { // fetch the screen if (m_screen_tag != nullptr) m_screen = machine().device(m_screen_tag); // allocate the timer m_timer = timer_alloc(); m_callback.bind_relative_to(*owner()); // register for save states save_item(NAME(m_first_time)); } //------------------------------------------------- // device_reset - reset the device //------------------------------------------------- void timer_device::device_reset() { // type based configuration switch (m_type) { case TIMER_TYPE_GENERIC: case TIMER_TYPE_PERIODIC: { // convert the period into attotime attotime period; if (m_period > attotime::zero) { period = m_period; // convert the start_delay into attotime attotime start_delay = attotime::zero; if (m_start_delay > attotime::zero) start_delay = m_start_delay; // allocate and start the backing timer m_timer->adjust(start_delay, m_param, period); } break; } case TIMER_TYPE_SCANLINE: if (m_screen == nullptr) fatalerror("timer '%s': unable to find screen '%s'\n", tag(), m_screen_tag); // set the timer to fire immediately m_first_time = true; m_timer->adjust(attotime::zero, m_param); break; } } //------------------------------------------------- // device_timer - handle timer expiration events //------------------------------------------------- void timer_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { switch (m_type) { // general periodic timers just call through case TIMER_TYPE_GENERIC: case TIMER_TYPE_PERIODIC: if (!m_callback.isnull()) (m_callback)(*this, m_ptr, param); break; // scanline timers have to do some additiona bookkeeping case TIMER_TYPE_SCANLINE: { // by default, we fire at the first position int next_vpos = m_first_vpos; // the first time through we just go with the default position if (!m_first_time) { // call the real callback int vpos = m_screen->vpos(); if (!m_callback.isnull()) (m_callback)(*this, m_ptr, vpos); // advance by the increment only if we will still be within the screen bounds if (m_increment != 0 && (vpos + m_increment) < m_screen->height()) next_vpos = vpos + m_increment; } m_first_time = false; // adjust the timer m_timer->adjust(m_screen->time_until_pos(next_vpos)); break; } } }