diff options
Diffstat (limited to 'src/emu/timer.c')
-rw-r--r-- | src/emu/timer.c | 810 |
1 files changed, 810 insertions, 0 deletions
diff --git a/src/emu/timer.c b/src/emu/timer.c new file mode 100644 index 00000000000..cd1b4fe3eb1 --- /dev/null +++ b/src/emu/timer.c @@ -0,0 +1,810 @@ +/*************************************************************************** + + timer.c + + Functions needed to generate timing and synchronization between several + CPUs. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include "driver.h" +#include "profiler.h" +#include "pool.h" +#include <math.h> + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define VERBOSE 0 + +#if VERBOSE +#define LOG(x) do { logerror x; } while (0) +#else +#define LOG(x) +#endif + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define MAX_TIMERS 256 + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* in timer.h: typedef struct _emu_timer emu_timer; */ +struct _emu_timer +{ + emu_timer * next; + emu_timer * prev; + void (*callback)(running_machine *, int); + void (*callback_ptr)(running_machine *, void *); + int callback_param; + void * callback_ptr_param; + const char * file; + int line; + const char * func; + UINT8 enabled; + UINT8 temporary; + UINT8 ptr; + attotime period; + attotime start; + attotime expire; +}; + + + +/*************************************************************************** + 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; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static void timer_postload(void); +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; +} + + +/*------------------------------------------------- + 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_func_postload(timer_postload); + 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]; +} + + +/*------------------------------------------------- + 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) +{ + return timer_head->expire; +} + + +/*------------------------------------------------- + timer_adjust_global_time - adjust the global + time; this is also where we fire the timers +-------------------------------------------------*/ + +void timer_set_global_time(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), attotime_string(timer_head->expire))); + + /* 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) + { + if (!timer->ptr && timer->callback) + { + 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->callback_param); + profiler_mark(PROFILER_END); + } + else if (timer->ptr && timer->callback_ptr) + { + 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_ptr)(Machine, timer->callback_ptr_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); + } + } + } +} + + + +/*************************************************************************** + 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->callback_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 void timer_postload(void) +{ + emu_timer *privlist = NULL; + emu_timer *t; + + /* remove all timers and make a private list */ + while (timer_head) + { + 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) + { + 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(void (*callback)(running_machine *, int), void (*callback_ptr)(running_machine *, void *), void *param, 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->callback_ptr = callback_ptr; + timer->callback_param = 0; + timer->callback_ptr_param = param; + timer->enabled = FALSE; + timer->temporary = temp; + timer->ptr = (callback_ptr != NULL); + 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 ourselve 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(void (*callback)(running_machine *, int), const char *file, int line, const char *func) +{ + return _timer_alloc_common(callback, NULL, NULL, file, line, func, FALSE); +} + +emu_timer *_timer_alloc_ptr(void (*callback_ptr)(running_machine *, void *), void *param, const char *file, int line, const char *func) +{ + return _timer_alloc_common(NULL, callback_ptr, param, 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 - adjust the time when this + timer will fire, and whether or not it will + fire periodically +-------------------------------------------------*/ + +INLINE void timer_adjust_common(emu_timer *which, attotime duration, 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->callback_param = param; + which->enabled = TRUE; + + /* clamp negative times to 0 */ + if (duration.seconds < 0) + duration = attotime_zero; + + /* set the start and expire times */ + which->start = time; + which->expire = attotime_add(time, duration); + 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 %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_adjust(emu_timer *which, attotime duration, INT32 param, attotime period) +{ + if (which->ptr) + fatalerror("timer_adjust called on a ptr timer!"); + timer_adjust_common(which, duration, param, period); +} + +void timer_adjust_ptr(emu_timer *which, attotime duration, attotime period) +{ + if (!which->ptr) + fatalerror("timer_adjust_ptr called on a non-ptr timer!"); + timer_adjust_common(which, duration, 0, period); +} + + + +/*************************************************************************** + SIMPLIFIED ANONYMOUS TIMER MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + timer_pulse - allocate a pulse timer, which + repeatedly calls the callback using the given + period +-------------------------------------------------*/ + +void _timer_pulse(attotime period, INT32 param, void (*callback)(running_machine *, int), const char *file, int line, const char *func) +{ + emu_timer *timer = _timer_alloc_common(callback, NULL, NULL, file, line, func, FALSE); + + /* adjust to our liking */ + timer_adjust(timer, period, param, period); +} + +void _timer_pulse_ptr(attotime period, void *param, void (*callback)(running_machine *, void *), const char *file, int line, const char *func) +{ + emu_timer *timer = _timer_alloc_common(NULL, callback, param, file, line, func, FALSE); + + /* adjust to our liking */ + timer_adjust_ptr(timer, period, period); +} + + +/*------------------------------------------------- + timer_set - allocate a one-shot timer, which + calls the callback after the given duration +-------------------------------------------------*/ + +void _timer_set(attotime duration, INT32 param, void (*callback)(running_machine *, int), const char *file, int line, const char *func) +{ + emu_timer *timer = _timer_alloc_common(callback, NULL, NULL, file, line, func, TRUE); + + /* adjust to our liking */ + timer_adjust(timer, duration, param, attotime_zero); +} + +void _timer_set_ptr(attotime duration, void *param, void (*callback)(running_machine *, void *), const char *file, int line, const char *func) +{ + emu_timer *timer = _timer_alloc_common(NULL, callback, param, file, line, func, TRUE); + + /* adjust to our liking */ + timer_adjust_ptr(timer, duration, attotime_zero); +} + + + +/*************************************************************************** + MISCELLANEOUS CONTROLS +***************************************************************************/ + +/*------------------------------------------------- + timer_reset - reset the timing on a timer +-------------------------------------------------*/ + +void timer_reset(emu_timer *which, attotime duration) +{ + /* adjust the timer */ + if (!which->ptr) + timer_adjust(which, duration, which->callback_param, which->period); + else + timer_adjust_ptr(which, duration, which->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; +} + + +/*------------------------------------------------- + timer_enabled - determine if a timer is + enabled +-------------------------------------------------*/ + +int timer_enabled(emu_timer *which) +{ + return which->enabled; +} + + +/*------------------------------------------------- + timer_get_param + timer_get_param_ptr - returns the callback + parameter of a timer +-------------------------------------------------*/ + +int timer_get_param(emu_timer *which) +{ + return which->callback_param; +} + + +void *timer_get_param_ptr(emu_timer *which) +{ + return which->callback_ptr_param; +} + + + +/*************************************************************************** + 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); +} + + +/*------------------------------------------------- + timer_timeleft - return the time until the + next trigger +-------------------------------------------------*/ + +attotime timer_timeleft(emu_timer *which) +{ + return attotime_sub(which->expire, get_current_time()); +} + + +/*------------------------------------------------- + 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; +} + + +/*------------------------------------------------- + timer_firetime - return the time when this + timer will fire next +-------------------------------------------------*/ + +attotime timer_firetime(emu_timer *which) +{ + return which->expire; +} + + + +/*************************************************************************** + 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"); +} |