--- SheepShaver/src/timer.cpp 2009/08/01 07:02:41 1.11 +++ SheepShaver/src/timer.cpp 2010/05/05 00:29:46 1.13 @@ -29,6 +29,10 @@ #include #endif +#ifdef PRECISE_TIMING_MACH +#include +#endif + #define DEBUG 0 #include "debug.h" @@ -49,11 +53,10 @@ enum { // TMTask struct struct TMDesc { uint32 task; // Mac address of associated TMTask tm_time_t wakeup; // Time this task is scheduled for execution - bool in_use; // Flag: descriptor in use + TMDesc *next; }; -const int NUM_DESCS = 64; // Maximum number of descriptors -static TMDesc desc[NUM_DESCS]; +static TMDesc *tmDescList; #if PRECISE_TIMING #ifdef PRECISE_TIMING_BEOS @@ -73,46 +76,47 @@ static tm_time_t wakeup_time = wakeup_ti static pthread_mutex_t wakeup_time_lock = PTHREAD_MUTEX_INITIALIZER; static void *timer_func(void *arg); #endif +#ifdef PRECISE_TIMING_MACH +static clock_serv_t system_clock; +static thread_act_t timer_thread; +static bool timer_thread_active = false; +static tm_time_t wakeup_time_max = { 0x7fffffff, 999999999 }; +static tm_time_t wakeup_time = wakeup_time_max; +static semaphore_t wakeup_time_sem; +static void *timer_func(void *arg); +#endif #endif -/* - * Allocate descriptor for given TMTask in list - */ - -static int alloc_desc(uint32 tm) +inline static void free_desc(TMDesc *desc) { - // Search for first free descriptor - for (int i=0; inext; + } else { + for (TMDesc *d = tmDescList; d; d = d->next) { + if (d->next == desc) { + d->next = desc->next; + break; + } } - return -1; -} - - -/* - * Free descriptor in list - */ - -inline static void free_desc(int i) -{ - desc[i].in_use = false; + } + delete desc; } - /* * Find descriptor associated with given TMTask */ -inline static int find_desc(uint32 tm) +inline static TMDesc *find_desc(uint32 tm) { - for (int i=0; itask == tm) { + return desc; + } + desc = desc->next; + } + return NULL; } @@ -258,9 +262,7 @@ static void timer_thread_resume(void) void TimerInit(void) { - // Mark all descriptors as inactive - for (int i=0; inext; + delete desc; + desc = desc->next; + } + tmDescList = NULL; } @@ -321,12 +338,13 @@ int16 InsTime(uint32 tm, uint16 trap) { D(bug("InsTime %08lx, trap %04x\n", tm, trap)); WriteMacInt16((uint32)tm + qType, ReadMacInt16((uint32)tm + qType) & 0x1fff | (trap << 4) & 0x6000); - if (find_desc(tm) >= 0) - printf("WARNING: InsTime(): Task re-inserted\n"); + if (find_desc(tm)) + printf("WARNING: InsTime(%08lx): Task re-inserted\n", tm); else { - int i = alloc_desc(tm); - if (i < 0) - printf("FATAL: InsTime(): No free Time Manager descriptor\n"); + TMDesc *desc = new TMDesc; + desc->task = tm; + desc->next = tmDescList; + tmDescList = desc; } return 0; } @@ -341,8 +359,8 @@ int16 RmvTime(uint32 tm) D(bug("RmvTime %08lx\n", tm)); // Find descriptor - int i = find_desc(tm); - if (i < 0) { + TMDesc *desc = find_desc(tm); + if (!desc) { printf("WARNING: RmvTime(%08lx): Descriptor not found\n", tm); return 0; } @@ -352,6 +370,10 @@ int16 RmvTime(uint32 tm) while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ; suspend_thread(timer_thread); #endif +#ifdef PRECISE_TIMING_MACH + semaphore_wait(wakeup_time_sem); + thread_suspend(timer_thread); +#endif #if PRECISE_TIMING_POSIX timer_thread_suspend(); pthread_mutex_lock(&wakeup_time_lock); @@ -364,17 +386,16 @@ int16 RmvTime(uint32 tm) #if PRECISE_TIMING // Look for next task to be called and set wakeup_time wakeup_time = wakeup_time_max; - for (int j=0; jnext) + if ((ReadMacInt16(d->task + qType) & 0x8000)) + if (timer_cmp_time(d->wakeup, wakeup_time) < 0) + wakeup_time = d->wakeup; #endif // Compute remaining time tm_time_t remaining, current; timer_current_time(current); - timer_sub_time(remaining, desc[i].wakeup, current); + timer_sub_time(remaining, desc->wakeup, current); WriteMacInt32(tm + tmCount, timer_host2mac_time(remaining)); } else WriteMacInt32(tm + tmCount, 0); @@ -387,6 +408,11 @@ int16 RmvTime(uint32 tm) get_thread_info(timer_thread, &info); } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?) #endif +#ifdef PRECISE_TIMING_MACH + semaphore_signal(wakeup_time_sem); + thread_abort(timer_thread); + thread_resume(timer_thread); +#endif #if PRECISE_TIMING_POSIX pthread_mutex_unlock(&wakeup_time_lock); timer_thread_resume(); @@ -394,7 +420,7 @@ int16 RmvTime(uint32 tm) #endif // Free descriptor - free_desc(i); + free_desc(desc); return 0; } @@ -408,9 +434,9 @@ int16 PrimeTime(uint32 tm, int32 time) D(bug("PrimeTime %08lx, time %ld\n", tm, time)); // Find descriptor - int i = find_desc(tm); - if (i < 0) { - printf("FATAL: PrimeTime(): Descriptor not found\n"); + TMDesc *desc = find_desc(tm); + if (!desc) { + printf("FATAL: PrimeTime(%08lx): Descriptor not found\n", tm); return 0; } @@ -434,15 +460,15 @@ int16 PrimeTime(uint32 tm, int32 time) // Yes, calculate wakeup time relative to last scheduled time tm_time_t wakeup; - timer_add_time(wakeup, desc[i].wakeup, delay); - desc[i].wakeup = wakeup; + timer_add_time(wakeup, desc->wakeup, delay); + desc->wakeup = wakeup; } else { // No, calculate wakeup time relative to current time tm_time_t now; timer_current_time(now); - timer_add_time(desc[i].wakeup, now, delay); + timer_add_time(desc->wakeup, now, delay); } // Set tmWakeUp to indicate that task was scheduled @@ -453,7 +479,7 @@ int16 PrimeTime(uint32 tm, int32 time) // Not extended task, calculate wakeup time relative to current time tm_time_t now; timer_current_time(now); - timer_add_time(desc[i].wakeup, now, delay); + timer_add_time(desc->wakeup, now, delay); } // Make task active and enqueue it in the Time Manager queue @@ -461,6 +487,10 @@ int16 PrimeTime(uint32 tm, int32 time) while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ; suspend_thread(timer_thread); #endif +#ifdef PRECISE_TIMING_MACH + semaphore_wait(wakeup_time_sem); + thread_suspend(timer_thread); +#endif #if PRECISE_TIMING_POSIX timer_thread_suspend(); pthread_mutex_lock(&wakeup_time_lock); @@ -470,11 +500,10 @@ int16 PrimeTime(uint32 tm, int32 time) #if PRECISE_TIMING // Look for next task to be called and set wakeup_time wakeup_time = wakeup_time_max; - for (int j=0; jnext) + if ((ReadMacInt16(d->task + qType) & 0x8000)) + if (timer_cmp_time(d->wakeup, wakeup_time) < 0) + wakeup_time = d->wakeup; #ifdef PRECISE_TIMING_BEOS release_sem(wakeup_time_sem); thread_info info; @@ -483,6 +512,11 @@ int16 PrimeTime(uint32 tm, int32 time) get_thread_info(timer_thread, &info); } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?) #endif +#ifdef PRECISE_TIMING_MACH + semaphore_signal(wakeup_time_sem); + thread_abort(timer_thread); + thread_resume(timer_thread); +#endif #ifdef PRECISE_TIMING_POSIX pthread_mutex_unlock(&wakeup_time_lock); timer_thread_resume(); @@ -519,11 +553,33 @@ static int32 timer_func(void *arg) } #endif +#ifdef PRECISE_TIMING_MACH +static void *timer_func(void *arg) +{ + timer_thread = mach_thread_self(); + timer_thread_active = true; + + while (timer_thread_active) { + clock_sleep(system_clock, TIME_ABSOLUTE, wakeup_time, NULL); + semaphore_wait(wakeup_time_sem); + + tm_time_t system_time; + + timer_current_time(system_time); + if (timer_cmp_time(wakeup_time, system_time) < 0) { + wakeup_time = wakeup_time_max; + SetInterruptFlag(INTFLAG_TIMER); + TriggerInterrupt(); + } + semaphore_signal(wakeup_time_sem); + } +} +#endif + #ifdef PRECISE_TIMING_POSIX static void *timer_func(void *arg) { while (!timer_thread_cancel) { - // Wait until time specified by wakeup_time clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &wakeup_time, NULL); @@ -555,27 +611,29 @@ void TimerInterrupt(void) // Look for active TMTasks that have expired tm_time_t now; timer_current_time(now); - for (int i=0; inext; + uint32 tm = desc->task; + if ((ReadMacInt16(tm + qType) & 0x8000) && timer_cmp_time(desc->wakeup, now) <= 0) { + + // Found one, mark as inactive and remove it from the Time Manager queue + WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) & 0x7fff); + dequeue_tm(tm); + + // Call timer function + uint32 addr = ReadMacInt32(tm + tmAddr); + if (addr) { + D(bug("Calling TimeTask %08lx, addr %08lx\n", tm, addr)); + M68kRegisters r; + r.a[0] = addr; + r.a[1] = tm; + Execute68k(r.a[0], &r); + D(bug(" returned from TimeTask\n")); } } + desc = next; + } #if PRECISE_TIMING // Look for next task to be called and set wakeup_time @@ -583,16 +641,19 @@ void TimerInterrupt(void) while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ; suspend_thread(timer_thread); #endif +#if PRECISE_TIMING_MACH + semaphore_wait(wakeup_time_sem); + thread_suspend(timer_thread); +#endif #if PRECISE_TIMING_POSIX timer_thread_suspend(); pthread_mutex_lock(&wakeup_time_lock); #endif wakeup_time = wakeup_time_max; - for (int j=0; jnext) + if ((ReadMacInt16(d->task + qType) & 0x8000)) + if (timer_cmp_time(d->wakeup, wakeup_time) < 0) + wakeup_time = d->wakeup; #if PRECISE_TIMING_BEOS release_sem(wakeup_time_sem); thread_info info; @@ -601,6 +662,11 @@ void TimerInterrupt(void) get_thread_info(timer_thread, &info); } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?) #endif +#if PRECISE_TIMING_MACH + semaphore_signal(wakeup_time_sem); + thread_abort(timer_thread); + thread_resume(timer_thread); +#endif #if PRECISE_TIMING_POSIX pthread_mutex_unlock(&wakeup_time_lock); timer_thread_resume();