summaryrefslogblamecommitdiffstatshomepage
path: root/src/emu/timer.c
blob: 3b1b8258f8120156b196ec6c58874d0a51842714 (plain) (tree)
1
2
3
4
5
6
7
8






                                                                            
                                                






                                                                            







                                                                            
                                                         







                                                                            
                                  









                                                                            

                                     
                                         

                                      




                                          





                                       








                                                                                               


                                                                                                                                
                                                                                                                     

                                                                                                                                


                                                                                                                   

  
 














                                                                                                                      



















                                                                            




                                             





                                                                            
                                        













































































































                                                                                                 














                                                               

















































                                                                                                                     
                                                                    











                                                







                                                             

























                                                                            









                                                                            
                                                                                                                           



                                                                                          







                                                   
                                                                      





                                       
                                                                                                                                      
















                                                                                                                                
                                                           
                 

                                                                                                                                               
                                                                              
                                                    

























                                                                                                   












































                                                                                                     
                                                                                         




























                                                                                                       

























                                                                            
                                                           














                                                                        
                                       




                                                       
                                  
















                                                                              
                                








































                                                                                                              
                                                                                                                                   





                                           

                         

                                









                                                                          
                                                                                   









                                                                              
                                                                                                                    
 
                                                                           
































                                                                            
                                                          
                                              

                                                   





                                                                           

                                                                                            
                 

                                                    

                                                    
      
 



                                                                             
                                                   

                                                 


                                                   
                                                                                                







                                                                          
                             


                                       

                                            


                                            
                                                        






                                                                                      
                                                                                                                                          



                                                              
 
                                                                                                                 

                                                   
                 

                                                    

                                                    
      
 
                                         



                               
                                                                                  


 










                                                                            
                                                                                                                                            
 
                                                                                       
                                                            

 





                                                   
                                                                                                                                            
 
                                                                                      
                                                     

 











                                                                            
                                                                            


 
                                                   

                                                   
                 

                                                    

                                                    
      
 
                                                                                  


 



















                                                            






                                                               










                                                   






                                                    
                                                   
                                          




                                                   
                            


 


                                                      
                 

                                                    

                                                    
      
 



                            






























                                                                  
 
                          


 
                                                      




                                                   
 


















                                                                














                                                                            






                                                             










                                                               






                                                          




















                                                   






                                                           










                                                   







                                                          
                                                


                                                   
                                                             
 


                                                    

                                    


















































                                                                                                                                


 






























                                                                                                                                                                         













                                                                            
                                                    
                            











                                                
                                                                                                                                 

                                         
                                        

                                 




                                                                                   

                             






















                                                                                                




                                                                                         
 

                                                                                    
 

                                                                 
 

                                                                                    
 














































                                                                                                      
         
 
                               
























                                                                                               
                                                                                                                      
















                                                                                                                                              
/***************************************************************************

    timer.c

    Functions needed to generate timing and synchronization between several
    CPUs.

    Copyright Nicola Salmoria and the MAME Team.
    Visit http://mamedev.org for licensing and usage restrictions.

***************************************************************************/

#include "driver.h"
#include "profiler.h"
#include "pool.h"


/***************************************************************************
    DEBUGGING
***************************************************************************/

#define VERBOSE 0

#define LOG(x)	do { if (VERBOSE) logerror x; } while (0)



/***************************************************************************
    CONSTANTS
***************************************************************************/

#define MAX_TIMERS		256
#define MAX_QUANTA		16



/***************************************************************************
    TYPE DEFINITIONS
***************************************************************************/

/* in timer.h: typedef struct _emu_timer emu_timer; */
struct _emu_timer
{
	emu_timer *		next;
	emu_timer *		prev;
	timer_fired_func	callback;
	INT32 			param;
	void *			ptr;
	const char *	file;
	int 			line;
	const char *	func;
	UINT8 			enabled;
	UINT8 			temporary;
	attotime 		period;
	attotime 		start;
	attotime 		expire;
};


/*-------------------------------------------------
    timer_state - configuration of a single
    timer device
-------------------------------------------------*/

typedef struct _timer_state timer_state;
struct _timer_state
{
	emu_timer				*timer;			/* the backing timer */
	void 					*ptr;			/* the pointer parameter passed to the timer callback */

	/* periodic timers only */
	attotime				start_delay;	/* delay before the timer fires for the first time */
	attotime				period;			/* period of repeated timer firings */
	INT32					param;			/* the integer parameter passed to the timer callback */

	/* scanline timers only */
	UINT32 					first_time;		/* indicates that the system is starting */
};


/*-------------------------------------------------
    quantum_slot - a single minimum quantum 
    request
-------------------------------------------------*/

typedef struct _quantum_slot quantum_slot;
struct _quantum_slot
{
	attoseconds_t			actual;			/* actual duration of the quantum */
	attoseconds_t			requested;		/* duration of the requested quantum */
	attotime				expire;			/* absolute expiration time of this quantum */
};



/***************************************************************************
    GLOBAL VARIABLES
***************************************************************************/

/* conversion constants */
attoseconds_t attoseconds_per_cycle[MAX_CPU];
UINT32 cycles_per_second[MAX_CPU];

/* list of active timers */
static emu_timer timers[MAX_TIMERS];
static emu_timer *timer_head;
static emu_timer *timer_free_head;
static emu_timer *timer_free_tail;

/* other internal states */
static attotime global_basetime;
static emu_timer *callback_timer;
static int callback_timer_modified;
static attotime callback_timer_expire_time;

/* scheduling quanta */
static quantum_slot quantum_list[MAX_QUANTA];
static quantum_slot *quantum_current;
static attoseconds_t quantum_minimum;



/***************************************************************************
    FUNCTION PROTOTYPES
***************************************************************************/

static STATE_POSTLOAD( timer_postload );
static void timer_logtimers(void);
static void timer_remove(emu_timer *which);



/***************************************************************************
    INLINE FUNCTIONS
***************************************************************************/

/*-------------------------------------------------
    get_current_time - return the current time
-------------------------------------------------*/

INLINE attotime get_current_time(void)
{
	int activecpu;

	/* if we're currently in a callback, use the timer's expiration time as a base */
	if (callback_timer != NULL)
		return callback_timer_expire_time;

	/* if we're executing as a particular CPU, use its local time as a base */
	activecpu = cpu_getactivecpu();
	if (activecpu >= 0)
		return cpunum_get_localtime(activecpu);

	/* otherwise, return the current global base time */
	return global_basetime;
}


/*-------------------------------------------------
    timer_new - allocate a new timer
-------------------------------------------------*/

INLINE emu_timer *timer_new(void)
{
	emu_timer *timer;

	/* remove an empty entry */
	if (!timer_free_head)
	{
		timer_logtimers();
		fatalerror("Out of timers!");
	}
	timer = timer_free_head;
	timer_free_head = timer->next;
	if (!timer_free_head)
		timer_free_tail = NULL;

	return timer;
}


/*-------------------------------------------------
    timer_list_insert - insert a new timer into
    the list at the appropriate location
-------------------------------------------------*/

INLINE void timer_list_insert(emu_timer *timer)
{
	attotime expire = timer->enabled ? timer->expire : attotime_never;
	emu_timer *t, *lt = NULL;

	/* sanity checks for the debug build */
	#ifdef MAME_DEBUG
	{
		int tnum = 0;

		/* loop over the timer list */
		for (t = timer_head; t; t = t->next, tnum++)
		{
			if (t == timer)
				fatalerror("This timer is already inserted in the list!");
			if (tnum == MAX_TIMERS-1)
				fatalerror("Timer list is full!");
		}
	}
	#endif

	/* loop over the timer list */
	for (t = timer_head; t; lt = t, t = t->next)
	{
		/* if the current list entry expires after us, we should be inserted before it */
		if (attotime_compare(t->expire, expire) > 0)
		{
			/* link the new guy in before the current list entry */
			timer->prev = t->prev;
			timer->next = t;

			if (t->prev)
				t->prev->next = timer;
			else
				timer_head = timer;
			t->prev = timer;
			return;
		}
	}

	/* need to insert after the last one */
	if (lt)
		lt->next = timer;
	else
		timer_head = timer;
	timer->prev = lt;
	timer->next = NULL;
}


/*-------------------------------------------------
    get_safe_token - makes sure that the passed
    in device is, in fact, a timer
-------------------------------------------------*/

INLINE timer_state *get_safe_token(const device_config *device)
{
	assert(device != NULL);
	assert(device->token != NULL);
	assert(device->type == TIMER);

	return (timer_state *)device->token;
}


/*-------------------------------------------------
    timer_list_remove - remove a timer from the
    linked list
-------------------------------------------------*/

INLINE void timer_list_remove(emu_timer *timer)
{
	/* sanity checks for the debug build */
	#ifdef MAME_DEBUG
	{
		emu_timer *t;

		/* loop over the timer list */
		for (t = timer_head; t && t != timer; t = t->next) ;
		if (t == NULL)
			fatalerror("timer (%s from %s:%d) not found in list", timer->func, timer->file, timer->line);
	}
	#endif

	/* remove it from the list */
	if (timer->prev)
		timer->prev->next = timer->next;
	else
		timer_head = timer->next;
	if (timer->next)
		timer->next->prev = timer->prev;
}



/***************************************************************************
    INITIALIZATION
***************************************************************************/

/*-------------------------------------------------
    timer_init - initialize the timer system
-------------------------------------------------*/

void timer_init(running_machine *machine)
{
	int i;

	/* we need to wait until the first call to timer_cyclestorun before using real CPU times */
	global_basetime = attotime_zero;
	callback_timer = NULL;
	callback_timer_modified = FALSE;

	/* register with the save state system */
	state_save_push_tag(0);
	state_save_register_item("timer", 0, global_basetime.seconds);
	state_save_register_item("timer", 0, global_basetime.attoseconds);
	state_save_register_postload(machine, timer_postload, NULL);
	state_save_pop_tag();

	/* reset the timers */
	memset(timers, 0, sizeof(timers));

	/* initialize the lists */
	timer_head = NULL;
	timer_free_head = &timers[0];
	for (i = 0; i < MAX_TIMERS-1; i++)
		timers[i].next = &timers[i+1];
	timers[MAX_TIMERS-1].next = NULL;
	timer_free_tail = &timers[MAX_TIMERS-1];
	
	/* reset the quanta */
	memset(quantum_list, 0, sizeof(quantum_list));
	quantum_list[0].requested = ATTOSECONDS_IN_MSEC(100);
	quantum_list[0].actual = ATTOSECONDS_IN_MSEC(100);
	quantum_list[0].expire = attotime_never;
	quantum_current = &quantum_list[0];
	quantum_minimum = ATTOSECONDS_IN_NSEC(1) / 1000;
}


/*-------------------------------------------------
    timer_destructor - destruct a timer from a
    pool callback
-------------------------------------------------*/

void timer_destructor(void *ptr, size_t size)
{
	timer_remove(ptr);
}



/***************************************************************************
    SCHEDULING HELPERS
***************************************************************************/

/*-------------------------------------------------
    timer_next_fire_time - return the
    time when the next timer will fire
-------------------------------------------------*/

attotime timer_next_fire_time(void)
{
	attotime quantum_time;
	
	/* if the current quantum has expired, find a new one */
	if (attotime_compare(global_basetime, quantum_current->expire) >= 0)
	{
		int curr;
		
		quantum_current->requested = 0;
		quantum_current = &quantum_list[0];
		for (curr = 1; curr < ARRAY_LENGTH(quantum_list); curr++)
			if (quantum_list[curr].requested != 0 && quantum_list[curr].requested < quantum_current->requested)
				quantum_current = &quantum_list[curr];
	}
	quantum_time = attotime_add_attoseconds(global_basetime, quantum_current->actual);
	return attotime_min(quantum_time, timer_head->expire);
}


/*-------------------------------------------------
    timer_adjust_global_time - adjust the global
    time; this is also where we fire the timers
-------------------------------------------------*/

void timer_set_global_time(running_machine *machine, attotime newbase)
{
	emu_timer *timer;

	/* set the new global offset */
	global_basetime = newbase;

	LOG(("timer_set_global_time: new=%s head->expire=%s\n", attotime_string(newbase, 9), attotime_string(timer_head->expire, 9)));

	/* now process any timers that are overdue */
	while (attotime_compare(timer_head->expire, global_basetime) <= 0)
	{
		int was_enabled = timer_head->enabled;

		/* if this is a one-shot timer, disable it now */
		timer = timer_head;
		if (attotime_compare(timer->period, attotime_zero) == 0 || attotime_compare(timer->period, attotime_never) == 0)
			timer->enabled = FALSE;

		/* set the global state of which callback we're in */
		callback_timer_modified = FALSE;
		callback_timer = timer;
		callback_timer_expire_time = timer->expire;

		/* call the callback */
		if (was_enabled && timer->callback != NULL)
		{
			LOG(("Timer %s:%d[%s] fired (expire=%s)\n", timer->file, timer->line, timer->func, attotime_string(timer->expire, 9)));
			profiler_mark(PROFILER_TIMER_CALLBACK);
			(*timer->callback)(machine, timer->ptr, timer->param);
			profiler_mark(PROFILER_END);
		}

		/* clear the callback timer global */
		callback_timer = NULL;

		/* reset or remove the timer, but only if it wasn't modified during the callback */
		if (!callback_timer_modified)
		{
			/* if the timer is temporary, remove it now */
			if (timer->temporary)
				timer_remove(timer);

			/* otherwise, reschedule it */
			else
			{
				timer->start = timer->expire;
				timer->expire = attotime_add(timer->expire, timer->period);

				timer_list_remove(timer);
				timer_list_insert(timer);
			}
		}
	}
}


/*-------------------------------------------------
    timer_add_scheduling_quantum - add a 
    scheduling quantum; the smallest active one 
    is the one that is in use
-------------------------------------------------*/

void timer_add_scheduling_quantum(running_machine *machine, attoseconds_t quantum, attotime duration)
{
	attotime curtime = timer_get_time();
	attotime expire = attotime_add(curtime, duration);
	int curr, blank = -1;

	/* a 0 request (minimum) needs to be non-zero to occupy a slot */
	if (quantum == 0)
		quantum = 1;
			
	/* find an equal-duration slot or an empty slot */
	for (curr = 1; curr < ARRAY_LENGTH(quantum_list); curr++)
	{
		quantum_slot *slot = &quantum_list[curr];
		
		/* look for a matching quantum and extend it */
		if (slot->requested == quantum)
		{
			slot->expire = attotime_max(slot->expire, expire);
			return;
		}
		
		/* remember any empty slots in case of no match */
		if (slot->requested == 0)
		{
			if (blank == -1)
				blank = curr;
		}

		/* otherwise, expire any expired slots */
		else if (attotime_compare(curtime, slot->expire) >= 0)
			slot->requested = 0;
	}
	
	/* fatal error if no slots left */
	assert_always(blank != -1, "Out of scheduling quantum slots!");

	/* fill in the item */
	quantum_list[blank].requested = quantum;
	quantum_list[blank].actual = MAX(quantum_list[blank].requested, quantum_minimum);
	quantum_list[blank].expire = expire;
	
	/* update the minimum */
	if (quantum < quantum_current->requested)
		quantum_current = &quantum_list[blank];
}


/*-------------------------------------------------
    timer_set_minimum_quantum - control the 
    minimum useful quantum (used by cpuexec only)
-------------------------------------------------*/

void timer_set_minimum_quantum(running_machine *machine, attoseconds_t quantum)
{
	int curr;
	
	/* do nothing if nothing changed */
	if (quantum_minimum == quantum)
		return;
	quantum_minimum = quantum;
	
	/* adjust all the actuals; this doesn't affect the current */
	for (curr = 0; curr < ARRAY_LENGTH(quantum_list); curr++)
		if (quantum_list[curr].requested != 0)
			quantum_list[curr].actual = MAX(quantum_list[curr].requested, quantum_minimum);
}



/***************************************************************************
    SAVE/RESTORE HELPERS
***************************************************************************/

/*-------------------------------------------------
    timer_register_save - register ourself with
    the save state system
-------------------------------------------------*/

static void timer_register_save(emu_timer *timer)
{
	char buf[256];
	int count = 0;
	emu_timer *t;

	/* find other timers that match our func name */
	for (t = timer_head; t; t = t->next)
		if (!strcmp(t->func, timer->func))
			count++;

	/* make up a name */
	sprintf(buf, "timer.%s", timer->func);

	/* use different instances to differentiate the bits */
	state_save_push_tag(0);
	state_save_register_item(buf, count, timer->param);
	state_save_register_item(buf, count, timer->enabled);
	state_save_register_item(buf, count, timer->period.seconds);
	state_save_register_item(buf, count, timer->period.attoseconds);
	state_save_register_item(buf, count, timer->start.seconds);
	state_save_register_item(buf, count, timer->start.attoseconds);
	state_save_register_item(buf, count, timer->expire.seconds);
	state_save_register_item(buf, count, timer->expire.attoseconds);
	state_save_pop_tag();
}


/*-------------------------------------------------
    timer_postload - after loading a save state
-------------------------------------------------*/

static STATE_POSTLOAD( timer_postload )
{
	emu_timer *privlist = NULL;
	emu_timer *t;

	/* remove all timers and make a private list */
	while (timer_head != NULL)
	{
		t = timer_head;

		/* temporary timers go away entirely */
		if (t->temporary)
			timer_remove(t);

		/* permanent ones get added to our private list */
		else
		{
			timer_list_remove(t);
			t->next = privlist;
			privlist = t;
		}
	}

	/* now add them all back in; this effectively re-sorts them by time */
	while (privlist != NULL)
	{
		t = privlist;
		privlist = t->next;
		timer_list_insert(t);
	}
}


/*-------------------------------------------------
    timer_count_anonymous - count the number of
    anonymous (non-saveable) timers
-------------------------------------------------*/

int timer_count_anonymous(void)
{
	emu_timer *t;
	int count = 0;

	logerror("timer_count_anonymous:\n");
	for (t = timer_head; t; t = t->next)
		if (t->temporary && t != callback_timer)
		{
			count++;
			logerror("  Temp. timer %p, file %s:%d[%s]\n", (void *) t, t->file, t->line, t->func);
		}
	logerror("%d temporary timers found\n", count);

	return count;
}



/***************************************************************************
    CORE TIMER ALLOCATION
***************************************************************************/

/*-------------------------------------------------
    timer_alloc - allocate a permament timer that
    isn't primed yet
-------------------------------------------------*/

INLINE emu_timer *_timer_alloc_common(timer_fired_func callback, void *ptr, const char *file, int line, const char *func, int temp)
{
	attotime time = get_current_time();
	emu_timer *timer = timer_new();

	/* fill in the record */
	timer->callback = callback;
	timer->ptr = ptr;
	timer->param = 0;
	timer->enabled = FALSE;
	timer->temporary = temp;
	timer->period = attotime_zero;
	timer->file = file;
	timer->line = line;
	timer->func = func;

	/* compute the time of the next firing and insert into the list */
	timer->start = time;
	timer->expire = attotime_never;
	timer_list_insert(timer);

	/* if we're not temporary, register ourselves with the save state system */
	if (!temp)
	{
		timer_register_save(timer);
		restrack_register_object(OBJTYPE_TIMER, timer, 0, file, line);
	}

	/* return a handle */
	return timer;
}

emu_timer *_timer_alloc_internal(timer_fired_func callback, void *ptr, const char *file, int line, const char *func)
{
	return _timer_alloc_common(callback, ptr, file, line, func, FALSE);
}


/*-------------------------------------------------
    timer_remove - remove a timer from the
    system
-------------------------------------------------*/

static void timer_remove(emu_timer *which)
{
	/* if this is a callback timer, note that */
	if (which == callback_timer)
		callback_timer_modified = TRUE;

	/* remove it from the list */
	timer_list_remove(which);

	/* free it up by adding it back to the free list */
	if (timer_free_tail)
		timer_free_tail->next = which;
	else
		timer_free_head = which;
	which->next = NULL;
	timer_free_tail = which;
}



/***************************************************************************
    CORE TIMER ADJUSTMENT
***************************************************************************/

/*-------------------------------------------------
    timer_adjust_oneshot - adjust the time when this timer
    will fire and disable any periodic firings
-------------------------------------------------*/

void timer_adjust_oneshot(emu_timer *which, attotime duration, INT32 param)
{
	timer_adjust_periodic(which, duration, param, attotime_never);
}


void timer_device_adjust_oneshot(const device_config *timer, attotime duration, INT32 param)
{
#ifdef MAME_DEBUG
	timer_config *config = timer->inline_config;

	/* doesn't make sense for scanline timers */
	assert(config->type != TIMER_TYPE_SCANLINE);
#endif

	timer_device_adjust_periodic(timer, duration, param, attotime_never);
}


/*-------------------------------------------------
    timer_adjust_periodic - adjust the time when
    this timer will fire and specify a period for
    subsequent firings
-------------------------------------------------*/

void timer_adjust_periodic(emu_timer *which, attotime start_delay, INT32 param, attotime period)
{
	attotime time = get_current_time();

	/* if this is the callback timer, mark it modified */
	if (which == callback_timer)
		callback_timer_modified = TRUE;

	/* compute the time of the next firing and insert into the list */
	which->param = param;
	which->enabled = TRUE;

	/* clamp negative times to 0 */
	if (start_delay.seconds < 0)
		start_delay = attotime_zero;

	/* set the start and expire times */
	which->start = time;
	which->expire = attotime_add(time, start_delay);
	which->period = period;

	/* remove and re-insert the timer in its new order */
	timer_list_remove(which);
	timer_list_insert(which);

	/* if this was inserted as the head, abort the current timeslice and resync */
	LOG(("timer_adjust_oneshot %s.%s:%d to expire @ %s\n", which->file, which->func, which->line, attotime_string(which->expire, 9)));
	if (which == timer_head && cpu_getexecutingcpu() >= 0)
		activecpu_abort_timeslice();
}


void timer_device_adjust_periodic(const device_config *timer, attotime start_delay, INT32 param, attotime period)
{
	timer_state *state = get_safe_token(timer);
#ifdef MAME_DEBUG
	timer_config *config = timer->inline_config;

	/* doesn't make sense for scanline timers */
	assert(config->type != TIMER_TYPE_SCANLINE);
#endif

	state->start_delay = start_delay;
	state->period = period;
	state->param = param;

	/* adjust the timer */
	timer_adjust_periodic(state->timer, state->start_delay, 0, state->period);
}



/***************************************************************************
    SIMPLIFIED ANONYMOUS TIMER MANAGEMENT
***************************************************************************/

/*-------------------------------------------------
    timer_pulse - allocate a pulse timer, which
    repeatedly calls the callback using the given
    period
-------------------------------------------------*/

void _timer_pulse_internal(attotime period, void *ptr, INT32 param, timer_fired_func callback, const char *file, int line, const char *func)
{
	emu_timer *timer = _timer_alloc_common(callback, ptr, file, line, func, FALSE);
	timer_adjust_periodic(timer, period, param, period);
}


/*-------------------------------------------------
    timer_set - allocate a one-shot timer, which
    calls the callback after the given duration
-------------------------------------------------*/

void _timer_set_internal(attotime duration, void *ptr, INT32 param, timer_fired_func callback, const char *file, int line, const char *func)
{
	emu_timer *timer = _timer_alloc_common(callback, ptr, file, line, func, TRUE);
	timer_adjust_oneshot(timer, duration, param);
}



/***************************************************************************
    MISCELLANEOUS CONTROLS
***************************************************************************/

/*-------------------------------------------------
    timer_reset - reset the timing on a timer
-------------------------------------------------*/

void timer_reset(emu_timer *which, attotime duration)
{
	timer_adjust_periodic(which, duration, which->param, which->period);
}


void timer_device_reset(const device_config *timer)
{
	timer_state *state = get_safe_token(timer);
#ifdef MAME_DEBUG
	timer_config *config = timer->inline_config;

	/* doesn't make sense for scanline timers */
	assert(config->type != TIMER_TYPE_SCANLINE);
#endif

	timer_adjust_periodic(state->timer, state->start_delay, 0, state->period);
}


/*-------------------------------------------------
    timer_enable - enable/disable a timer
-------------------------------------------------*/

int timer_enable(emu_timer *which, int enable)
{
	int old;

	/* set the enable flag */
	old = which->enabled;
	which->enabled = enable;

	/* remove the timer and insert back into the list */
	timer_list_remove(which);
	timer_list_insert(which);

	return old;
}


int timer_device_enable(const device_config *timer, int enable)
{
	timer_state *state = get_safe_token(timer);
	return timer_enable(state->timer, enable);
}


/*-------------------------------------------------
    timer_enabled - determine if a timer is
    enabled
-------------------------------------------------*/

int timer_enabled(emu_timer *which)
{
	return which->enabled;
}


int timer_device_enabled(const device_config *timer)
{
	timer_state *state = get_safe_token(timer);
	return timer_enabled(state->timer);
}


/*-------------------------------------------------
    timer_get_param - returns the callback
    parameter of a timer
-------------------------------------------------*/

int timer_get_param(emu_timer *which)
{
	return which->param;
}


int timer_device_get_param(const device_config *timer)
{
	timer_state *state = get_safe_token(timer);
#ifdef MAME_DEBUG
	timer_config *config = timer->inline_config;

	/* doesn't make sense for scanline timers */
	assert(config->type != TIMER_TYPE_SCANLINE);
#endif

	return state->param;
}


/*-------------------------------------------------
    timer_set_param - changes the callback
    parameter of a timer
-------------------------------------------------*/

void timer_set_param(emu_timer *which, int param)
{
	which->param = param;
}


void timer_device_set_param(const device_config *timer, int param)
{
	timer_state *state = get_safe_token(timer);
#ifdef MAME_DEBUG
	timer_config *config = timer->inline_config;

	/* doesn't make sense for scanline timers */
	assert(config->type != TIMER_TYPE_SCANLINE);
#endif

	state->param = param;
}


/*-------------------------------------------------
    timer_get_ptr - returns the callback pointer
    of a timer
-------------------------------------------------*/

void *timer_get_ptr(emu_timer *which)
{
	return which->ptr;
}


void *timer_device_get_ptr(const device_config *timer)
{
	timer_state *state = get_safe_token(timer);
	return state->ptr;
}


/*-------------------------------------------------
    timer_set_ptr - changes the callback pointer
    of a timer
-------------------------------------------------*/

void timer_set_ptr(emu_timer *which, void *ptr)
{
	which->ptr = ptr;
}


void timer_device_set_ptr(const device_config *timer, void *ptr)
{
	timer_state *state = get_safe_token(timer);
	state->ptr = ptr;
}



/***************************************************************************
    TIMING FUNCTIONS
***************************************************************************/

/*-------------------------------------------------
    timer_timeelapsed - return the time since the
    last trigger
-------------------------------------------------*/

attotime timer_timeelapsed(emu_timer *which)
{
	return attotime_sub(get_current_time(), which->start);
}


attotime timer_device_timeelapsed(const device_config *timer)
{
	timer_state *state = get_safe_token(timer);
	return timer_timeelapsed(state->timer);
}


/*-------------------------------------------------
    timer_timeleft - return the time until the
    next trigger
-------------------------------------------------*/

attotime timer_timeleft(emu_timer *which)
{
	return attotime_sub(which->expire, get_current_time());
}


attotime timer_device_timeleft(const device_config *timer)
{
	timer_state *state = get_safe_token(timer);
	return timer_timeleft(state->timer);
}


/*-------------------------------------------------
    timer_get_time - return the current time
-------------------------------------------------*/

attotime timer_get_time(void)
{
	return get_current_time();
}


/*-------------------------------------------------
    timer_starttime - return the time when this
    timer started counting
-------------------------------------------------*/

attotime timer_starttime(emu_timer *which)
{
	return which->start;
}


attotime timer_device_starttime(const device_config *timer)
{
	timer_state *state = get_safe_token(timer);
	return timer_starttime(state->timer);
}


/*-------------------------------------------------
    timer_firetime - return the time when this
    timer will fire next
-------------------------------------------------*/

attotime timer_firetime(emu_timer *which)
{
	return which->expire;
}


attotime timer_device_firetime(const device_config *timer)
{
	timer_state *state = get_safe_token(timer);
	return timer_firetime(state->timer);
}


/*-------------------------------------------------
    periodic_timer_device_timer_callback - calls
    the timer device specific callback
-------------------------------------------------*/

static TIMER_CALLBACK( periodic_timer_device_timer_callback )
{
	const device_config *timer = ptr;
	timer_state *state = get_safe_token(timer);
	timer_config *config = timer->inline_config;

	/* call the real callback */
	config->callback(timer, state->ptr, state->param);
}



/*-------------------------------------------------
    scanline_timer_device_timer_callback -
    manages the scanline based timer's state
-------------------------------------------------*/

static TIMER_CALLBACK( scanline_timer_device_timer_callback )
{
	int next_vpos;
	const device_config *timer = ptr;
	timer_state *state = get_safe_token(timer);
	timer_config *config = timer->inline_config;

	/* get the screen device and verify it */
	const device_config *screen = device_list_find_by_tag(timer->machine->config->devicelist, VIDEO_SCREEN, config->screen);
	assert(screen != NULL);

	/* first time, start with the first scanline, but do not call the callback */
	if (state->first_time)
	{
		next_vpos = config->first_vpos;

		/* indicate that we are done with the first call */
		state->first_time = FALSE;
	}

	/* not the first time */
	else
	{
		int vpos = video_screen_get_vpos(screen);

		/* call the real callback */
		config->callback(timer, state->ptr, vpos);

		/* if the increment is 0 or the next scanline is larger than the screen size,
           go back to the first one */
        if ((config->increment == 0) ||
            ((vpos + config->increment) >= video_screen_get_height(screen)))
			next_vpos = config->first_vpos;

		/* otherwise, increment */
		else
			next_vpos = vpos + config->increment;
	}

	/* adjust the timer */
	timer_adjust_oneshot(state->timer, video_screen_get_time_until_pos(screen, next_vpos, 0), 0);
}



/***************************************************************************
    DEBUGGING
***************************************************************************/

/*-------------------------------------------------
    timer_logtimers - log all the timers
-------------------------------------------------*/

static void timer_logtimers(void)
{
	emu_timer *t;

	logerror("===============\n");
	logerror("TIMER LOG START\n");
	logerror("===============\n");

	logerror("Enqueued timers:\n");
	for (t = timer_head; t; t = t->next)
		logerror("  Start=%15.6f Exp=%15.6f Per=%15.6f Ena=%d Tmp=%d (%s:%d[%s])\n",
			attotime_to_double(t->start), attotime_to_double(t->expire), attotime_to_double(t->period), t->enabled, t->temporary, t->file, t->line, t->func);

	logerror("Free timers:\n");
	for (t = timer_free_head; t; t = t->next)
		logerror("  Start=%15.6f Exp=%15.6f Per=%15.6f Ena=%d Tmp=%d (%s:%d[%s])\n",
			attotime_to_double(t->start), attotime_to_double(t->expire), attotime_to_double(t->period), t->enabled, t->temporary, t->file, t->line, t->func);

	logerror("==============\n");
	logerror("TIMER LOG STOP\n");
	logerror("==============\n");
}



/***************************************************************************
    TIMER DEVICE INTERFACE
***************************************************************************/

/*-------------------------------------------------
    timer_start - device start callback
    for a timer device
-------------------------------------------------*/

static DEVICE_START( timer )
{
	timer_state *state = get_safe_token(device);
	char unique_tag[50];
	timer_config *config;
	void *param;

	/* validate some basic stuff */
	assert(device != NULL);
	assert(device->static_config == NULL);
	assert(device->inline_config != NULL);
	assert(device->machine != NULL);
	assert(device->machine->config != NULL);

	/* get and validate the configuration */
	config = device->inline_config;
	assert(config->type == TIMER_TYPE_PERIODIC || config->type == TIMER_TYPE_SCANLINE || config->type == TIMER_TYPE_GENERIC);
	assert(config->callback != NULL);

	/* copy the pointer parameter */
	state->ptr = config->ptr;

	/* create the name for save states */
	assert(strlen(device->tag) < 30);
	state_save_combine_module_and_tag(unique_tag, "timer_device", device->tag);

	/* type based configuration */
	switch (config->type)
	{
		case TIMER_TYPE_GENERIC:
			/* make sure that only the applicable parameters are filled in */
			assert(config->screen == NULL);
			assert(config->first_vpos == 0);
			assert(config->increment == 0);
			assert(config->start_delay == 0);
			assert(config->period == 0);

			/* copy the optional integer parameter */
			state->param = config->param;

			/* convert the start_delay and period into attotime */
			state->period = attotime_never;
			state->start_delay = attotime_zero;

			/* register for state saves */
			state_save_register_item(unique_tag, 0, state->param);

			/* allocate the backing timer */
			param = (void *)device;
			state->timer = timer_alloc(periodic_timer_device_timer_callback, param);
			break;

		case TIMER_TYPE_PERIODIC:
			/* make sure that only the applicable parameters are filled in */
			assert(config->screen == NULL);
			assert(config->first_vpos == 0);
			assert(config->increment == 0);

			/* validate that we have at least a start_delay or period */
			assert(config->period > 0);

			/* copy the optional integer parameter */
			state->param = config->param;

			/* convert the start_delay and period into attotime */
			state->period = UINT64_ATTOTIME_TO_ATTOTIME(config->period);

			if (config->start_delay > 0)
				state->start_delay = UINT64_ATTOTIME_TO_ATTOTIME(config->start_delay);
			else
				state->start_delay = attotime_zero;

			/* register for state saves */
			state_save_register_item(unique_tag, 0, state->start_delay.seconds);
			state_save_register_item(unique_tag, 0, state->start_delay.attoseconds);
			state_save_register_item(unique_tag, 0, state->period.seconds);
			state_save_register_item(unique_tag, 0, state->period.attoseconds);
			state_save_register_item(unique_tag, 0, state->param);

			/* allocate the backing timer */
			param = (void *)device;
			state->timer = timer_alloc(periodic_timer_device_timer_callback, param);

			/* finally, start the timer */
			timer_adjust_periodic(state->timer, state->start_delay, 0, state->period);
			break;

		case TIMER_TYPE_SCANLINE:
			/* make sure that only the applicable parameters are filled in */
			assert(config->start_delay == 0);
			assert(config->period == 0);
			assert(config->param == 0);

			assert(config->first_vpos >= 0);
			assert(config->increment >= 0);

			/* allocate the backing timer */
			param = (void *)device;
			state->timer = timer_alloc(scanline_timer_device_timer_callback, param);

			/* indicate that this will be the first call */
			state->first_time = TRUE;

			/* register for state saves */
			state_save_register_item(unique_tag, 0, state->first_time);

			/* fire it as soon as the emulation starts */
			timer_adjust_oneshot(state->timer, attotime_zero, 0);
			break;

		default:
			/* we will never get here */
			fatalerror("Unknown timer device type");
			break;
	}

	return DEVICE_START_OK;
}


/*-------------------------------------------------
    timer_set_info - device set info callback
-------------------------------------------------*/

static DEVICE_SET_INFO( timer )
{
	switch (state)
	{
		/* no parameters to set */
	}
}


/*-------------------------------------------------
    timer_get_info - device get info callback
-------------------------------------------------*/

DEVICE_GET_INFO( timer )
{
	switch (state)
	{
		/* --- the following bits of info are returned as 64-bit signed integers --- */
		case DEVINFO_INT_TOKEN_BYTES:			info->i = sizeof(timer_state);			break;
		case DEVINFO_INT_INLINE_CONFIG_BYTES:	info->i = sizeof(timer_config);			break;
		case DEVINFO_INT_CLASS:					info->i = DEVICE_CLASS_TIMER;			break;

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case DEVINFO_FCT_SET_INFO:				info->set_info = DEVICE_SET_INFO_NAME(timer); break;
		case DEVINFO_FCT_START:					info->start = DEVICE_START_NAME(timer); break;
		case DEVINFO_FCT_STOP:					/* Nothing */							break;
		case DEVINFO_FCT_RESET:					/* Nothing */							break;

		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case DEVINFO_STR_NAME:					info->s = "Generic";					break;
		case DEVINFO_STR_FAMILY:				info->s = "Timer";						break;
		case DEVINFO_STR_VERSION:				info->s = "1.0";						break;
		case DEVINFO_STR_SOURCE_FILE:			info->s = __FILE__;						break;
		case DEVINFO_STR_CREDITS:				info->s = "Copyright Nicola Salmoria and the MAME Team"; break;
	}
}