2003-02-15 00:05:36 +03:00
|
|
|
#include "bochs.h"
|
|
|
|
|
|
|
|
bx_virt_timer_c bx_virt_timer;
|
|
|
|
|
2003-02-16 07:58:43 +03:00
|
|
|
const Bit64u bx_pc_system_c::NullTimerInterval = BX_MAX_VIRTUAL_TIME;
|
|
|
|
|
2003-02-15 00:05:36 +03:00
|
|
|
static void
|
|
|
|
bx_virt_timer_c::nullTimer(void* this_ptr) {
|
|
|
|
UNUSED(this_ptr);
|
|
|
|
}
|
|
|
|
|
2003-02-16 02:39:41 +03:00
|
|
|
void periodic(Bit64u time_passed) {
|
|
|
|
//Assert that we haven't skipped any events.
|
2003-02-16 07:58:43 +03:00
|
|
|
BX_ASSERT (time_passed <= timers_next_event_time);
|
|
|
|
BX_ASSERT(!in_timer_handler);
|
2003-02-16 02:39:41 +03:00
|
|
|
|
|
|
|
//Update time variables.
|
2003-02-16 07:58:43 +03:00
|
|
|
timers_next_event_time -= time_passed;
|
|
|
|
current_timers_time += time_passed;
|
2003-02-16 02:39:41 +03:00
|
|
|
|
|
|
|
//If no events are occurring, just pass the time and we're done.
|
2003-02-16 07:58:43 +03:00
|
|
|
if( time_passed < timers_next_event_time ) {
|
2003-02-16 02:39:41 +03:00
|
|
|
return;
|
|
|
|
}
|
2003-02-16 07:58:43 +03:00
|
|
|
//Starting timer handler calls.
|
|
|
|
in_timer_handler = 1;
|
2003-02-16 02:39:41 +03:00
|
|
|
//Otherwise, cause any events to occur that should.
|
|
|
|
for(i=0;i<numTimers;i++) {
|
|
|
|
if( timer[i].inUse && timer[i].active ) {
|
|
|
|
//Assert that we haven't skipped any timers.
|
2003-02-16 07:58:43 +03:00
|
|
|
BX_ASSERT(current_timers_time <= timer[i].timeToFire);
|
|
|
|
if(timer[i].timeToFire == current_timers_time) {
|
2003-02-16 02:39:41 +03:00
|
|
|
if(timer[i].continuous) {
|
|
|
|
timer[i].timeToFire+=timer[i].period;
|
|
|
|
} else {
|
|
|
|
timer[i].active = 0;
|
|
|
|
}
|
|
|
|
//This function MUST return, or the timer mechanism
|
|
|
|
// will be broken.
|
|
|
|
timer[i].funct(this_ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2003-02-16 07:58:43 +03:00
|
|
|
//Finished timer handler calls.
|
|
|
|
in_timer_handler = 0;
|
2003-02-16 02:39:41 +03:00
|
|
|
//Use a second FOR loop so that a timer function call can
|
|
|
|
// change the behavior of another timer.
|
2003-02-16 07:58:43 +03:00
|
|
|
//timers_next_event_time normally contains a cycle count, not a cycle time.
|
2003-02-16 02:39:41 +03:00
|
|
|
// here we use it as a temporary variable that IS a cycle time,
|
|
|
|
// but then convert it back to a cycle count afterwards.
|
2003-02-16 07:58:43 +03:00
|
|
|
timers_next_event_time = current_timers_time + BX_MAX_VIRTUAL_TIME;
|
2003-02-16 02:39:41 +03:00
|
|
|
for(i=0;i<numTimers;i++) {
|
2003-02-16 07:58:43 +03:00
|
|
|
if( timer[i].inUse && timer[i].active && ((timer[i].timeToFire)<timers_next_event_time) ) {
|
|
|
|
timers_next_event_time = timer[i].timeToFire;
|
2003-02-16 02:39:41 +03:00
|
|
|
}
|
|
|
|
}
|
2003-02-16 07:58:43 +03:00
|
|
|
timers_next_event_time-=current_timers_time;
|
2003-02-16 02:39:41 +03:00
|
|
|
//FIXME
|
|
|
|
}
|
|
|
|
|
2003-02-15 00:05:36 +03:00
|
|
|
|
|
|
|
//Get the current virtual time.
|
|
|
|
// This may return the same value on subsequent calls.
|
|
|
|
Bit64u
|
2003-02-16 07:58:43 +03:00
|
|
|
bx_virt_timer_c::time_usec(void) {
|
2003-02-15 00:05:36 +03:00
|
|
|
if(!use_virtual_timers) {
|
|
|
|
return bx_pc_system.time_usec();
|
|
|
|
}
|
2003-02-16 02:39:41 +03:00
|
|
|
|
|
|
|
//I might choose to update the time here, but this is
|
|
|
|
// safer, since it prevents call stack loops.
|
2003-02-16 07:58:43 +03:00
|
|
|
return current_timers_time;
|
2003-02-15 00:05:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//Get the current virtual time.
|
|
|
|
// This will return a monotonically increasing value.
|
2003-02-16 02:39:41 +03:00
|
|
|
// MUST NOT be called from within a timer interrupt.
|
2003-02-15 00:05:36 +03:00
|
|
|
Bit64u
|
|
|
|
bx_virt_timer_c::time_usec_sequential(void) {
|
|
|
|
if(!use_virtual_timers) {
|
|
|
|
return bx_pc_system.time_usec_sequential();
|
|
|
|
}
|
2003-02-16 02:39:41 +03:00
|
|
|
|
|
|
|
//Can't prevent call stack loops here, so this
|
2003-02-16 07:58:43 +03:00
|
|
|
// MUST NOT be called from within a timer handler.
|
|
|
|
BX_ASSERT(timers_next_event_time>0);
|
|
|
|
BX_ASSERT(!in_timer_handler);
|
|
|
|
|
|
|
|
if(last_sequential_time >= current_timers_time) {
|
|
|
|
periodic(1);
|
|
|
|
last_sequential_time = current_timers_time;
|
|
|
|
}
|
|
|
|
return current_timers_time;
|
2003-02-15 00:05:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Register a timer handler to go off after a given interval.
|
|
|
|
//Register a timer handler to go off with a periodic interval.
|
|
|
|
int
|
|
|
|
bx_virt_timer_c::register_timer( void *this_ptr, bx_timer_handler_t handler,
|
|
|
|
Bit32u useconds,
|
|
|
|
bx_bool continuous, bx_bool active,
|
|
|
|
const char *id) {
|
|
|
|
if(!use_virtual_timers) {
|
|
|
|
return bx_pc_system.register_timer(this_ptr, handler, useconds,
|
|
|
|
continuous, active, id);
|
|
|
|
}
|
2003-02-16 02:39:41 +03:00
|
|
|
|
|
|
|
//We don't like starting with a zero period timer.
|
|
|
|
BX_ASSERT((!active) || (period>0));
|
|
|
|
|
|
|
|
//Search for an unused timer.
|
|
|
|
int i;
|
|
|
|
for (i=0; i < numTimers; i++) {
|
|
|
|
if (timer[i].inUse == 0 || i==numTimers)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// If we didn't find a free slot, increment the bound, numTimers.
|
|
|
|
if (i==numTimers)
|
|
|
|
numTimers++; // One new timer installed.
|
|
|
|
BX_ASSERT(numTimers<BX_MAX_VIRTUAL_TIMERS);
|
|
|
|
|
|
|
|
timer[i].inUse = 1;
|
|
|
|
timer[i].period = useconds;
|
2003-02-16 07:58:43 +03:00
|
|
|
timer[i].timeToFire = current_timers_time + (Bit64u)useconds;
|
2003-02-16 02:39:41 +03:00
|
|
|
timer[i].active = active;
|
|
|
|
timer[i].continuous = continuous;
|
|
|
|
timer[i].funct = handler;
|
|
|
|
timer[i].this_ptr = this_ptr;
|
|
|
|
strncpy(timer[i].id, id, BxMaxTimerIDLen);
|
|
|
|
id[BxMaxTimerIDLen]=0; //I like null terminated strings.
|
|
|
|
|
2003-02-16 07:58:43 +03:00
|
|
|
if(period < timers_next_event_time) {
|
|
|
|
timers_next_event_time = period;
|
2003-02-16 02:39:41 +03:00
|
|
|
//FIXME
|
|
|
|
}
|
2003-02-15 00:05:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//unregister a previously registered timer.
|
|
|
|
unsigned
|
|
|
|
bx_virt_timer_c::unregisterTimer(int timerID) {
|
|
|
|
if(!use_virtual_timers) {
|
|
|
|
return bx_pc_system.unregisterTimer(timerID);
|
|
|
|
}
|
2003-02-16 07:58:43 +03:00
|
|
|
|
|
|
|
BX_ASSERT(timerID >= 0);
|
|
|
|
BX_ASSERT(timerID < BX_MAX_VIRTUAL_TIMERS);
|
|
|
|
|
|
|
|
//No need to prevent doing this to unused timers.
|
2003-02-16 02:39:41 +03:00
|
|
|
timer[timerID].inUse = 0;
|
2003-02-15 00:05:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bx_virt_timer_c::start_timers(void) {
|
|
|
|
if(!use_virtual_timers) {
|
|
|
|
return bx_pc_system.start_timers();
|
|
|
|
}
|
2003-02-16 02:39:41 +03:00
|
|
|
//FIXME
|
2003-02-15 00:05:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//activate a deactivated but registered timer.
|
|
|
|
void
|
|
|
|
bx_virt_timer_c::activate_timer( unsigned timer_index, Bit32u useconds,
|
|
|
|
bx_bool continuous ) {
|
|
|
|
if(!use_virtual_timers) {
|
|
|
|
return bx_pc_system.activate_timer(timer_index, useconds, continuous);
|
|
|
|
}
|
2003-02-16 07:58:43 +03:00
|
|
|
|
|
|
|
BX_ASSERT(timer_index >= 0);
|
|
|
|
BX_ASSERT(timer_index < BX_MAX_VIRTUAL_TIMERS);
|
|
|
|
|
|
|
|
BX_ASSERT(timer[timer_index].inUse);
|
|
|
|
|
|
|
|
timer[timer_index].period=useconds;
|
|
|
|
timer[timer_index].timeToFire = current_timers_time + (Bit64u)useconds;
|
|
|
|
timer[timer_index].active=1;
|
|
|
|
timer[timer_index].continuous=continuous;
|
|
|
|
|
|
|
|
if(period < timers_next_event_time) {
|
|
|
|
timers_next_event_time = period;
|
|
|
|
//FIXME
|
|
|
|
}
|
2003-02-15 00:05:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//deactivate (but don't unregister) a currently registered timer.
|
|
|
|
void
|
|
|
|
bx_virt_timer_c::deactivate_timer( unsigned timer_index ) {
|
|
|
|
if(!use_virtual_timers) {
|
|
|
|
return bx_pc_system.deactivate_timer(timer_index);
|
|
|
|
}
|
2003-02-16 07:58:43 +03:00
|
|
|
|
|
|
|
BX_ASSERT(timer_index >= 0);
|
|
|
|
BX_ASSERT(timer_index < BX_MAX_VIRTUAL_TIMERS);
|
|
|
|
|
|
|
|
//No need to prevent doing this to unused/inactive timers.
|
2003-02-16 02:39:41 +03:00
|
|
|
timer[timer_index].active = 0;
|
2003-02-15 00:05:36 +03:00
|
|
|
}
|
|
|
|
|
2003-02-16 07:58:43 +03:00
|
|
|
void
|
|
|
|
bx_virt_timer_c::advance_virtual_time(Bit64u time_passed) {
|
|
|
|
ASSERT(time_passed <= virtual_next_event_time);
|
|
|
|
|
|
|
|
current_virtual_time += time_passed;
|
|
|
|
virtual_next_event_time -= time_passed;
|
|
|
|
|
|
|
|
if(current_virtual_time > current_timers_time) {
|
|
|
|
periodic(current_virtual_time - current_timers_time);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Called when next_event_time changes.
|
|
|
|
void
|
|
|
|
bx_virt_timer_c::next_event_time_update(void) {
|
|
|
|
virtual_next_event_time = timers_next_event_time + current_timers_time - current_virtual_time;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bx_virt_timer_c::init(void) {
|
|
|
|
numTimers = 0;
|
|
|
|
current_timers_time = 0;
|
|
|
|
timers_next_event_time = BX_MAX_VIRTUAL_TIME;
|
|
|
|
last_sequential_time = 0;
|
|
|
|
in_timer_handler = 0;
|
|
|
|
virtual_next_event_time = 0;
|
|
|
|
current_virtual_time = 0;
|
|
|
|
|
|
|
|
use_virtual_timers = BX_USE_VIRTUAL_TIMERS;
|
|
|
|
virtual_timers_realtime = BX_VIRTUAL_TIMERS_REALTIME;
|
|
|
|
|
|
|
|
register_timer(this, nullTimer, NullTimerInterval, 1, 1, "Null Timer");
|
|
|
|
|
|
|
|
system_timer_id = bx_pc_system.register_timer(this, pc_system_timer_handler, BX_MAX_VIRTUAL_TIME, 0, 1, "Virtual Timer");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bx_virt_timer_c::timer_handler(void) {
|
|
|
|
if(!virtual_timers_realtime) {
|
|
|
|
Bit64u temp_final_time = bx_pc_system.time_usec();
|
|
|
|
temp_final_time-=current_virtual_time;
|
|
|
|
while(temp_final_time) {
|
|
|
|
if((temp_final_time)>(virtual_next_event_time)) {
|
|
|
|
temp_final_time-=virtual_next_event_time;
|
|
|
|
advance_virtual_time(virtual_next_event_time);
|
|
|
|
} else {
|
|
|
|
advance_virtual_time(temp_final_time);
|
|
|
|
temp_final_time-=temp_final_time;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bx_pc_system.activate_timer(system_timer_id,
|
|
|
|
(virtual_next_event_time>2)?(virtual_next_event_time-2):1,
|
|
|
|
0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bx_virt_timer_c::pc_system_timer_handler(void* this_ptr) {
|
|
|
|
((bx_virt_timer_c *)this_ptr)->timer_handler();
|
|
|
|
}
|
|
|
|
|