// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************
timer.cpp
Timer devices.
***************************************************************************/
#include "emu.h"
#include "machine/timer.h"
/***************************************************************************
// DEBUGGING
***************************************************************************/
#define VERBOSE 0
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
//**************************************************************************
// LIVE TIMER DEVICE
//**************************************************************************
DEFINE_DEVICE_TYPE(TIMER, timer_device, "timer", "Timer")
//-------------------------------------------------
// timer_device - constructor
//-------------------------------------------------
timer_device::timer_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, TIMER, tag, owner, clock),
m_type(TIMER_TYPE_GENERIC),
m_callback(*this),
m_ptr(nullptr),
m_start_delay(attotime::zero),
m_period(attotime::zero),
m_param(0),
m_screen(*this, finder_base::DUMMY_TAG),
m_first_vpos(0),
m_increment(0),
m_timer(nullptr),
m_first_time(true)
{
}
//-------------------------------------------------
// 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.finder_tag() != finder_base::DUMMY_TAG || 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.finder_tag() != finder_base::DUMMY_TAG || 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_screen.finder_tag() == finder_base::DUMMY_TAG)
osd_printf_error("Scanline timer has no screen specified\n");
else if (!m_screen)
osd_printf_error("Scanline timer specifies nonexistent screen %s\n", m_screen.finder_tag());
// 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()
{
// allocate the timer
m_timer = timer_alloc();
m_callback.resolve();
// 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)
fatalerror("timer '%s': unable to find screen '%s'\n", tag(), m_screen.finder_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 additional 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;
}
}
}