// timer.cpp // // by David Hershberger // // Implements the Timer class. #include #include #include #include "timer.h" char *IN_DOS = NULL; static Timer *the_timer = NULL; void timer_handler(void) { Callback *call; // if(*IN_DOS) // return; if(!the_timer->changing_list){ if(!the_timer->doing_callback_list){ the_timer->doing_callback_list = 1; for(call = the_timer->callback_list; call; call = call->next){ the_timer->doing_this_callback = call; call->tick_count++; if(call->tick_count >= call->num_ticks){ // If it's time, call the callback with the approximate time // in seconds since it was last called. (call->function)(the_timer->tick_interval * (double) call->tick_count); call->tick_count = 0; } } the_timer->doing_callback_list = 0; } else{ // If we interrupted the section above, go ahead and increment all // the callbacks' tick_counts except for the one we were working // on. for(call = the_timer->callback_list; call; call = call->next) if(call != the_timer->doing_this_callback) call->tick_count++; } } } Timer::Timer() { changing_list = 0; doing_callback_list = 0; doing_this_callback = NULL; callback_list = NULL; } void Timer::setup() { if(the_timer != NULL){ fprintf(stderr, "Timer::setup(): multiple instantiations of class Timer not allowed.\n"); exit(1); } // set IN_DOS pointer so we can tell whether we're in DOS or not. // call dos interrupt 21h, 34h (INDOS) here to set IN_DOS, a // pointer to the a flag telling whether we've interrupted a DOS // interrupt or not. union REGS inregs, outregs; struct SREGS sregs; inregs.h.ah = 0x34; sregs.ds = 0; sregs.es = 0; intdosx(&inregs, &outregs, &sregs); fprintf(stderr, "outregs.x.ebx = %08x, sregs.es = %08x\n", outregs.x.ebx, sregs.es); IN_DOS = (char *) (outregs.x.ebx + (sregs.es << 4)); fprintf(stderr, "IN_DOS = %08x\n", IN_DOS); install_timer_handler(timer_handler); set_tick_interval(); the_timer = this; } Timer::~Timer() { Callback *list, *tmp; if(doing_callback_list){ fprintf(stderr, "Timer::~Timer(): can't destroy Timer from within a callback.\n"); exit(1); } doing_callback_list = 1; for(list = callback_list; list;){ tmp = list; list = list->next; delete tmp; } the_timer = NULL; } double Timer::read_tick_interval() { if(the_timer == NULL) setup(); return tick_interval; } void Timer::add_callback(void (* callback_fn)(double), int num_ticks, int offset) { Callback *new_callback = new Callback; if(the_timer == NULL) setup(); new_callback->next = callback_list; new_callback->function = callback_fn; new_callback->tick_count = offset; new_callback->num_ticks = num_ticks; if(doing_callback_list){ fprintf(stderr, "Timer::add_callback(): can't add a callback from inside a callback.\n"); exit(1); } // add the new callback, with protection. changing_list = 1; callback_list = new_callback; changing_list = 0; } void Timer::delete_callback(void (* callback_fn)(double), int num_ticks) { Callback **call_ptr, *tmp; for(call_ptr = &callback_list; *call_ptr;){ if((*call_ptr)->function == callback_fn && (*call_ptr)->num_ticks == num_ticks){ // protect against interrupts changing_list = 1; // delete the list entry. tmp = *call_ptr; *call_ptr = (*call_ptr)->next; delete tmp; changing_list = 0; } else call_ptr = &(*call_ptr)->next; } }