--- SheepShaver/src/timer.cpp 2008/01/01 09:47:38 1.8 +++ SheepShaver/src/timer.cpp 2011/12/28 18:14:58 1.15 @@ -18,10 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* - * TODO: Prime(0) - */ - #include "sysdeps.h" #include "timer.h" #include "macos_util.h" @@ -33,6 +29,10 @@ #include #endif +#ifdef PRECISE_TIMING_MACH +#include +#endif + #define DEBUG 0 #include "debug.h" @@ -53,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 @@ -77,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; } @@ -262,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 = next; + } + tmDescList = NULL; } @@ -325,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; } @@ -345,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; } @@ -356,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); @@ -368,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); @@ -391,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(); @@ -398,7 +420,7 @@ int16 RmvTime(uint32 tm) #endif // Free descriptor - free_desc(i); + free_desc(desc); return 0; } @@ -412,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; } @@ -428,24 +450,25 @@ int16 PrimeTime(uint32 tm, int32 time) // Yes, tmWakeUp set? if (ReadMacInt32(tm + tmWakeUp)) { - //!! PrimeTime(0) means continue previous delay - // (save wakeup time in RmvTime?) + // PrimeTime(0) can either mean (a) "the task runs as soon as interrupts are enabled" + // or (b) "continue previous delay" if an expired task was stopped via RmvTime() and + // then re-installed using InsXTime(). Since tmWakeUp was set, this is case (b). + // The remaining time was saved in tmCount by RmvTime(). if (time == 0) { - printf("FATAL: Unsupported PrimeTime(0)\n"); - return 0; + timer_mac2host_time(delay, ReadMacInt16(tm + tmCount)); } // 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 @@ -456,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 @@ -464,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); @@ -473,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; @@ -486,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(); @@ -522,11 +553,34 @@ 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); + } + return NULL; +} +#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); @@ -558,27 +612,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 @@ -586,16 +642,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; @@ -604,6 +663,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();