ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/timer.cpp
Revision: 1.12
Committed: 2009-08-17T20:44:30Z (14 years, 8 months ago) by asvitkine
Branch: MAIN
Changes since 1.11: +74 -1 lines
Log Message:
[Charles Srstka]
Attached is a set of patches to port the precise timer that is currently used in the Linux and BeOS builds of SheepShaver to Mac OS X (and any other Mach-based operating systems).

Currently, the Linux build uses the clock_gettime() function to get nanosecond-precision time, and falls back on gettimeofday() if it is not present. Unfortunately, Mac OS X does not currently support clock_gettime(), and gettimeofday() has only microsecond granularity. The Mach kernel, however, has a clock_get_time() function that does very nearly the same thing as clock_gettime(). The patches to BasiliskII cause the timing functions such as timer_current_time() to use clock_get_time() instead of gettimeofday() on Mach-based systems that do not support clock_gettime().

The changes to SheepShaver involve the precise timer. The existing code for Linux uses pthreads and real-time signals to handle the timing. Mac OS X unfortunately does not seem to support real-time signals, so Mach calls are again used to suspend and resume the timer thread in order to attempt to duplicate the Linux and BeOS versions of the timer. The code is somewhat ugly right now, as I decided to leave alone the pre-existing style of the source file, which unfortunately involves #ifdefs scattered throughout the file and some duplication of code. A future patch may want to clean this up to separate out the OS-specific code and put it all together at the top of the file. However, for the time being, this seems to work.

This has not been extensively tested, because I have not been able to get my hands on a good test-case app for the classic Mac OS that would run inside the emulator and try out the timer. However, performance does seem to be better than with the pre-existing code, and nothing seems to have blown up as far as I can tell. I did find a game via a Google search -  Cap'n Magneto - that is known to have problems with Basilisk/SheepShaver's legacy 60 Hz timer, and the opening fade-to-color for this game appears to run much more smoothly with the precise timer code in place.

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * timer.cpp - Time Manager emulation
3     *
4 gbeauche 1.8 * SheepShaver (C) 1997-2008 Christian Bauer and Marc Hellwig
5 cebix 1.1 *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     #include "sysdeps.h"
22     #include "timer.h"
23     #include "macos_util.h"
24     #include "main.h"
25     #include "cpu_emulation.h"
26    
27 gbeauche 1.4 #ifdef PRECISE_TIMING_POSIX
28     #include <pthread.h>
29     #include <semaphore.h>
30     #endif
31    
32 asvitkine 1.12 #ifdef PRECISE_TIMING_MACH
33     #include <mach/mach.h>
34     #endif
35    
36 cebix 1.1 #define DEBUG 0
37     #include "debug.h"
38    
39    
40     #define TM_QUEUE 0 // Enable TMQueue management (doesn't work)
41    
42    
43     // Definitions for Time Manager
44     enum { // TMTask struct
45     tmAddr = 6,
46     tmCount = 10,
47     tmWakeUp = 14,
48     tmReserved = 18
49     };
50    
51    
52     // Array of additional info for each installed TMTask
53     struct TMDesc {
54     uint32 task; // Mac address of associated TMTask
55     tm_time_t wakeup; // Time this task is scheduled for execution
56     bool in_use; // Flag: descriptor in use
57     };
58    
59     const int NUM_DESCS = 64; // Maximum number of descriptors
60     static TMDesc desc[NUM_DESCS];
61    
62     #if PRECISE_TIMING
63 gbeauche 1.4 #ifdef PRECISE_TIMING_BEOS
64 cebix 1.1 static thread_id timer_thread = -1;
65     static bool thread_active = true;
66 gbeauche 1.4 static const tm_time_t wakeup_time_max = 0x7fffffffffffffff;
67     static volatile tm_time_t wakeup_time = wakeup_time_max;
68 cebix 1.1 static sem_id wakeup_time_sem = -1;
69     static int32 timer_func(void *arg);
70     #endif
71 gbeauche 1.4 #ifdef PRECISE_TIMING_POSIX
72     static pthread_t timer_thread;
73 gbeauche 1.5 static bool timer_thread_active = false;
74     static volatile bool timer_thread_cancel = false;
75 gbeauche 1.4 static tm_time_t wakeup_time_max = { 0x7fffffff, 999999999 };
76     static tm_time_t wakeup_time = wakeup_time_max;
77 gbeauche 1.6 static pthread_mutex_t wakeup_time_lock = PTHREAD_MUTEX_INITIALIZER;
78 gbeauche 1.4 static void *timer_func(void *arg);
79     #endif
80 asvitkine 1.12 #ifdef PRECISE_TIMING_MACH
81     static clock_serv_t system_clock;
82     static thread_act_t timer_thread;
83     static bool timer_thread_active = false;
84     static tm_time_t wakeup_time_max = { 0x7fffffff, 999999999 };
85     static tm_time_t wakeup_time = wakeup_time_max;
86     static semaphore_t wakeup_time_sem;
87     static void *timer_func(void *arg);
88     #endif
89 gbeauche 1.4 #endif
90 cebix 1.1
91    
92     /*
93     * Allocate descriptor for given TMTask in list
94     */
95    
96     static int alloc_desc(uint32 tm)
97     {
98     // Search for first free descriptor
99     for (int i=0; i<NUM_DESCS; i++)
100     if (!desc[i].in_use) {
101     desc[i].task = tm;
102     desc[i].in_use = true;
103     return i;
104     }
105     return -1;
106     }
107    
108    
109     /*
110     * Free descriptor in list
111     */
112    
113     inline static void free_desc(int i)
114     {
115     desc[i].in_use = false;
116     }
117    
118    
119     /*
120     * Find descriptor associated with given TMTask
121     */
122    
123     inline static int find_desc(uint32 tm)
124     {
125     for (int i=0; i<NUM_DESCS; i++)
126     if (desc[i].in_use && desc[i].task == tm)
127     return i;
128     return -1;
129     }
130    
131    
132     /*
133     * Enqueue task in Time Manager queue
134     */
135    
136     static void enqueue_tm(uint32 tm)
137     {
138     #if TM_QUEUE
139     uint32 tm_var = ReadMacInt32(0xb30);
140     WriteMacInt32(tm + qLink, ReadMacInt32(tm_var));
141     WriteMacInt32(tm_var, tm);
142     #endif
143     }
144    
145    
146     /*
147     * Remove task from Time Manager queue
148     */
149    
150     static void dequeue_tm(uint32 tm)
151     {
152     #if TM_QUEUE
153     uint32 p = ReadMacInt32(0xb30);
154     while (p) {
155     uint32 next = ReadMacInt32(p + qLink);
156     if (next == tm) {
157     WriteMacInt32(p + qLink, ReadMacInt32(next + qLink));
158     return;
159     }
160     }
161     #endif
162     }
163    
164    
165     /*
166 gbeauche 1.4 * Timer thread operations
167     */
168    
169     #ifdef PRECISE_TIMING_POSIX
170     const int SIGSUSPEND = SIGRTMIN + 6;
171     const int SIGRESUME = SIGRTMIN + 7;
172     static struct sigaction sigsuspend_action;
173     static struct sigaction sigresume_action;
174    
175     static int suspend_count = 0;
176     static pthread_mutex_t suspend_count_lock = PTHREAD_MUTEX_INITIALIZER;
177     static sem_t suspend_ack_sem;
178 gbeauche 1.5 static sigset_t suspend_handler_mask;
179 gbeauche 1.4
180     // Signal handler for suspended thread
181     static void sigsuspend_handler(int sig)
182     {
183     sem_post(&suspend_ack_sem);
184 gbeauche 1.5 sigsuspend(&suspend_handler_mask);
185 gbeauche 1.4 }
186    
187     // Signal handler for resumed thread
188     static void sigresume_handler(int sig)
189     {
190     /* simply trigger a signal to stop clock_nanosleep() */
191     }
192    
193     // Initialize timer thread
194     static bool timer_thread_init(void)
195     {
196     // Install suspend signal handler
197 gbeauche 1.6 sigemptyset(&sigsuspend_action.sa_mask);
198     sigaddset(&sigsuspend_action.sa_mask, SIGRESUME);
199 gbeauche 1.4 sigsuspend_action.sa_handler = sigsuspend_handler;
200     sigsuspend_action.sa_flags = SA_RESTART;
201     #ifdef HAVE_SIGNAL_SA_RESTORER
202     sigsuspend_action.sa_restorer = NULL;
203     #endif
204     if (sigaction(SIGSUSPEND, &sigsuspend_action, NULL) < 0)
205     return false;
206    
207     // Install resume signal handler
208 gbeauche 1.6 sigemptyset(&sigresume_action.sa_mask);
209 gbeauche 1.4 sigresume_action.sa_handler = sigresume_handler;
210     sigresume_action.sa_flags = SA_RESTART;
211     #ifdef HAVE_SIGNAL_SA_RESTORER
212     sigresume_action.sa_restorer = NULL;
213     #endif
214     if (sigaction(SIGRESUME, &sigresume_action, NULL) < 0)
215     return false;
216    
217     // Initialize semaphore
218     if (sem_init(&suspend_ack_sem, 0, 0) < 0)
219     return false;
220    
221 gbeauche 1.5 // Initialize suspend_handler_mask, it excludes SIGRESUME
222     if (sigfillset(&suspend_handler_mask) != 0)
223     return false;
224     if (sigdelset(&suspend_handler_mask, SIGRESUME) != 0)
225     return false;
226    
227 gbeauche 1.4 // Create thread in running state
228     suspend_count = 0;
229     return (pthread_create(&timer_thread, NULL, timer_func, NULL) == 0);
230     }
231    
232     // Kill timer thread
233     static void timer_thread_kill(void)
234     {
235 gbeauche 1.5 timer_thread_cancel = true;
236 gbeauche 1.4 #ifdef HAVE_PTHREAD_CANCEL
237     pthread_cancel(timer_thread);
238     #endif
239     pthread_join(timer_thread, NULL);
240     }
241    
242     // Suspend timer thread
243     static void timer_thread_suspend(void)
244     {
245     pthread_mutex_lock(&suspend_count_lock);
246     if (suspend_count == 0) {
247     suspend_count ++;
248     if (pthread_kill(timer_thread, SIGSUSPEND) == 0)
249     sem_wait(&suspend_ack_sem);
250     }
251     pthread_mutex_unlock(&suspend_count_lock);
252     }
253    
254     // Resume timer thread
255     static void timer_thread_resume(void)
256     {
257     pthread_mutex_lock(&suspend_count_lock);
258     assert(suspend_count > 0);
259     if (suspend_count == 1) {
260     suspend_count = 0;
261     pthread_kill(timer_thread, SIGRESUME);
262     }
263     pthread_mutex_unlock(&suspend_count_lock);
264     }
265     #endif
266    
267    
268     /*
269 cebix 1.1 * Initialize Time Manager
270     */
271    
272     void TimerInit(void)
273     {
274     // Mark all descriptors as inactive
275     for (int i=0; i<NUM_DESCS; i++)
276     free_desc(i);
277    
278     #if PRECISE_TIMING
279     // Start timer thread
280 gbeauche 1.4 #ifdef PRECISE_TIMING_BEOS
281 cebix 1.1 wakeup_time_sem = create_sem(1, "Wakeup Time");
282     timer_thread = spawn_thread(timer_func, "Time Manager", B_REAL_TIME_PRIORITY, NULL);
283     resume_thread(timer_thread);
284 asvitkine 1.12 #elif PRECISE_TIMING_MACH
285     pthread_t pthread;
286    
287     host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &system_clock);
288     semaphore_create(mach_task_self(), &wakeup_time_sem, SYNC_POLICY_FIFO, 1);
289    
290     pthread_create(&pthread, NULL, &timer_func, NULL);
291 cebix 1.1 #endif
292 gbeauche 1.4 #ifdef PRECISE_TIMING_POSIX
293 gbeauche 1.5 timer_thread_active = timer_thread_init();
294 gbeauche 1.4 #endif
295     #endif
296 cebix 1.1 }
297    
298    
299     /*
300     * Exit Time Manager
301     */
302    
303     void TimerExit(void)
304     {
305     #if PRECISE_TIMING
306     // Quit timer thread
307     if (timer_thread > 0) {
308 gbeauche 1.4 #ifdef PRECISE_TIMING_BEOS
309 cebix 1.1 status_t l;
310     thread_active = false;
311     suspend_thread(timer_thread);
312     resume_thread(timer_thread);
313     wait_for_thread(timer_thread, &l);
314     delete_sem(wakeup_time_sem);
315 gbeauche 1.4 #endif
316 asvitkine 1.12 #ifdef PRECISE_TIMING_MACH
317     timer_thread_active = false;
318     semaphore_destroy(mach_task_self(), wakeup_time_sem);
319     #endif
320 gbeauche 1.4 #ifdef PRECISE_TIMING_POSIX
321     timer_thread_kill();
322     #endif
323 cebix 1.1 }
324     #endif
325     }
326    
327    
328     /*
329     * Emulator reset, remove all timer tasks
330     */
331    
332     void TimerReset(void)
333     {
334     // Mark all descriptors as inactive
335     for (int i=0; i<NUM_DESCS; i++)
336     free_desc(i);
337     }
338    
339    
340     /*
341     * Insert timer task
342     */
343    
344     int16 InsTime(uint32 tm, uint16 trap)
345     {
346     D(bug("InsTime %08lx, trap %04x\n", tm, trap));
347     WriteMacInt16((uint32)tm + qType, ReadMacInt16((uint32)tm + qType) & 0x1fff | (trap << 4) & 0x6000);
348     if (find_desc(tm) >= 0)
349     printf("WARNING: InsTime(): Task re-inserted\n");
350     else {
351     int i = alloc_desc(tm);
352     if (i < 0)
353     printf("FATAL: InsTime(): No free Time Manager descriptor\n");
354     }
355     return 0;
356     }
357    
358    
359     /*
360     * Remove timer task
361     */
362    
363     int16 RmvTime(uint32 tm)
364     {
365     D(bug("RmvTime %08lx\n", tm));
366    
367     // Find descriptor
368     int i = find_desc(tm);
369     if (i < 0) {
370     printf("WARNING: RmvTime(%08lx): Descriptor not found\n", tm);
371     return 0;
372     }
373    
374     // Task active?
375 gbeauche 1.4 #if PRECISE_TIMING_BEOS
376 cebix 1.1 while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
377     suspend_thread(timer_thread);
378     #endif
379 asvitkine 1.12 #ifdef PRECISE_TIMING_MACH
380     semaphore_wait(wakeup_time_sem);
381     thread_suspend(timer_thread);
382     #endif
383 gbeauche 1.4 #if PRECISE_TIMING_POSIX
384     timer_thread_suspend();
385 gbeauche 1.6 pthread_mutex_lock(&wakeup_time_lock);
386 gbeauche 1.4 #endif
387 cebix 1.1 if (ReadMacInt16(tm + qType) & 0x8000) {
388    
389     // Yes, make task inactive and remove it from the Time Manager queue
390     WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) & 0x7fff);
391     dequeue_tm(tm);
392     #if PRECISE_TIMING
393     // Look for next task to be called and set wakeup_time
394 gbeauche 1.4 wakeup_time = wakeup_time_max;
395 cebix 1.1 for (int j=0; j<NUM_DESCS; j++) {
396     if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
397 gbeauche 1.4 if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
398 cebix 1.1 wakeup_time = desc[j].wakeup;
399     }
400     #endif
401    
402     // Compute remaining time
403     tm_time_t remaining, current;
404     timer_current_time(current);
405     timer_sub_time(remaining, desc[i].wakeup, current);
406     WriteMacInt32(tm + tmCount, timer_host2mac_time(remaining));
407     } else
408     WriteMacInt32(tm + tmCount, 0);
409     D(bug(" tmCount %ld\n", ReadMacInt32(tm + tmCount)));
410 gbeauche 1.4 #if PRECISE_TIMING_BEOS
411 cebix 1.1 release_sem(wakeup_time_sem);
412     thread_info info;
413     do {
414     resume_thread(timer_thread); // This will unblock the thread
415     get_thread_info(timer_thread, &info);
416     } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
417     #endif
418 asvitkine 1.12 #ifdef PRECISE_TIMING_MACH
419     semaphore_signal(wakeup_time_sem);
420     thread_abort(timer_thread);
421     thread_resume(timer_thread);
422     #endif
423 gbeauche 1.4 #if PRECISE_TIMING_POSIX
424 gbeauche 1.6 pthread_mutex_unlock(&wakeup_time_lock);
425 gbeauche 1.4 timer_thread_resume();
426     assert(suspend_count == 0);
427     #endif
428 cebix 1.1
429     // Free descriptor
430     free_desc(i);
431     return 0;
432     }
433    
434    
435     /*
436     * Start timer task
437     */
438    
439     int16 PrimeTime(uint32 tm, int32 time)
440     {
441     D(bug("PrimeTime %08lx, time %ld\n", tm, time));
442    
443     // Find descriptor
444     int i = find_desc(tm);
445     if (i < 0) {
446     printf("FATAL: PrimeTime(): Descriptor not found\n");
447     return 0;
448     }
449    
450     // Convert delay time
451     tm_time_t delay;
452     timer_mac2host_time(delay, time);
453    
454     // Extended task?
455     if (ReadMacInt16(tm + qType) & 0x4000) {
456    
457     // Yes, tmWakeUp set?
458     if (ReadMacInt32(tm + tmWakeUp)) {
459    
460 asvitkine 1.9 // PrimeTime(0) can either mean (a) "the task runs as soon as interrupts are enabled"
461     // or (b) "continue previous delay" if an expired task was stopped via RmvTime() and
462 asvitkine 1.10 // then re-installed using InsXTime(). Since tmWakeUp was set, this is case (b).
463     // The remaining time was saved in tmCount by RmvTime().
464 asvitkine 1.11 if (time == 0) {
465     timer_mac2host_time(delay, ReadMacInt16(tm + tmCount));
466     }
467 cebix 1.1
468     // Yes, calculate wakeup time relative to last scheduled time
469     tm_time_t wakeup;
470     timer_add_time(wakeup, desc[i].wakeup, delay);
471     desc[i].wakeup = wakeup;
472    
473     } else {
474    
475     // No, calculate wakeup time relative to current time
476     tm_time_t now;
477     timer_current_time(now);
478     timer_add_time(desc[i].wakeup, now, delay);
479     }
480    
481     // Set tmWakeUp to indicate that task was scheduled
482     WriteMacInt32(tm + tmWakeUp, 0x12345678);
483    
484     } else {
485    
486     // Not extended task, calculate wakeup time relative to current time
487     tm_time_t now;
488     timer_current_time(now);
489     timer_add_time(desc[i].wakeup, now, delay);
490     }
491    
492     // Make task active and enqueue it in the Time Manager queue
493 gbeauche 1.4 #if PRECISE_TIMING_BEOS
494 cebix 1.1 while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
495     suspend_thread(timer_thread);
496     #endif
497 asvitkine 1.12 #ifdef PRECISE_TIMING_MACH
498     semaphore_wait(wakeup_time_sem);
499     thread_suspend(timer_thread);
500     #endif
501 gbeauche 1.4 #if PRECISE_TIMING_POSIX
502     timer_thread_suspend();
503 gbeauche 1.6 pthread_mutex_lock(&wakeup_time_lock);
504 gbeauche 1.4 #endif
505 cebix 1.1 WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) | 0x8000);
506     enqueue_tm(tm);
507     #if PRECISE_TIMING
508     // Look for next task to be called and set wakeup_time
509 gbeauche 1.4 wakeup_time = wakeup_time_max;
510 cebix 1.1 for (int j=0; j<NUM_DESCS; j++) {
511     if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
512 gbeauche 1.4 if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
513 cebix 1.1 wakeup_time = desc[j].wakeup;
514     }
515 gbeauche 1.4 #ifdef PRECISE_TIMING_BEOS
516 cebix 1.1 release_sem(wakeup_time_sem);
517     thread_info info;
518     do {
519     resume_thread(timer_thread); // This will unblock the thread
520     get_thread_info(timer_thread, &info);
521     } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
522     #endif
523 asvitkine 1.12 #ifdef PRECISE_TIMING_MACH
524     semaphore_signal(wakeup_time_sem);
525     thread_abort(timer_thread);
526     thread_resume(timer_thread);
527     #endif
528 gbeauche 1.4 #ifdef PRECISE_TIMING_POSIX
529 gbeauche 1.6 pthread_mutex_unlock(&wakeup_time_lock);
530 gbeauche 1.4 timer_thread_resume();
531     assert(suspend_count == 0);
532     #endif
533     #endif
534 cebix 1.1 return 0;
535     }
536    
537    
538     /*
539     * Time Manager thread
540     */
541    
542 gbeauche 1.4 #ifdef PRECISE_TIMING_BEOS
543 cebix 1.1 static int32 timer_func(void *arg)
544     {
545     while (thread_active) {
546    
547     // Wait until time specified by wakeup_time
548     snooze_until(wakeup_time, B_SYSTEM_TIMEBASE);
549    
550     while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
551     if (wakeup_time < system_time()) {
552    
553     // Timer expired, trigger interrupt
554     wakeup_time = 0x7fffffffffffffff;
555     SetInterruptFlag(INTFLAG_TIMER);
556     TriggerInterrupt();
557     }
558     release_sem(wakeup_time_sem);
559     }
560     return 0;
561     }
562     #endif
563    
564 asvitkine 1.12 #ifdef PRECISE_TIMING_MACH
565     static void *timer_func(void *arg)
566     {
567     timer_thread = mach_thread_self();
568     timer_thread_active = true;
569    
570     while (timer_thread_active) {
571     clock_sleep(system_clock, TIME_ABSOLUTE, wakeup_time, NULL);
572     semaphore_wait(wakeup_time_sem);
573    
574     tm_time_t system_time;
575    
576     timer_current_time(system_time);
577     if (timer_cmp_time(wakeup_time, system_time) < 0) {
578     wakeup_time = wakeup_time_max;
579     SetInterruptFlag(INTFLAG_TIMER);
580     TriggerInterrupt();
581     }
582     semaphore_signal(wakeup_time_sem);
583     }
584     }
585     #endif
586    
587 gbeauche 1.4 #ifdef PRECISE_TIMING_POSIX
588     static void *timer_func(void *arg)
589     {
590 gbeauche 1.5 while (!timer_thread_cancel) {
591 gbeauche 1.4 // Wait until time specified by wakeup_time
592     clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &wakeup_time, NULL);
593    
594     tm_time_t system_time;
595     timer_current_time(system_time);
596     if (timer_cmp_time(wakeup_time, system_time) < 0) {
597    
598     // Timer expired, trigger interrupt
599 gbeauche 1.7 pthread_mutex_lock(&wakeup_time_lock);
600 gbeauche 1.4 wakeup_time = wakeup_time_max;
601 gbeauche 1.7 pthread_mutex_unlock(&wakeup_time_lock);
602 gbeauche 1.4 SetInterruptFlag(INTFLAG_TIMER);
603     TriggerInterrupt();
604     }
605     }
606     return NULL;
607     }
608     #endif
609    
610 cebix 1.1
611     /*
612     * Timer interrupt function (executed as part of 60Hz interrupt)
613     */
614    
615     void TimerInterrupt(void)
616     {
617     // D(bug("TimerIRQ\n"));
618    
619     // Look for active TMTasks that have expired
620     tm_time_t now;
621     timer_current_time(now);
622     for (int i=0; i<NUM_DESCS; i++)
623     if (desc[i].in_use) {
624     uint32 tm = desc[i].task;
625     if ((ReadMacInt16(tm + qType) & 0x8000) && timer_cmp_time(desc[i].wakeup, now) <= 0) {
626    
627     // Found one, mark as inactive and remove it from the Time Manager queue
628     WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) & 0x7fff);
629     dequeue_tm(tm);
630    
631     // Call timer function
632     uint32 addr = ReadMacInt32(tm + tmAddr);
633     if (addr) {
634     D(bug("Calling TimeTask %08lx, addr %08lx\n", tm, addr));
635     M68kRegisters r;
636     r.a[0] = addr;
637     r.a[1] = tm;
638     Execute68k(r.a[0], &r);
639     D(bug(" returned from TimeTask\n"));
640     }
641     }
642     }
643    
644     #if PRECISE_TIMING
645     // Look for next task to be called and set wakeup_time
646 gbeauche 1.4 #if PRECISE_TIMING_BEOS
647 cebix 1.1 while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
648     suspend_thread(timer_thread);
649 gbeauche 1.4 #endif
650 asvitkine 1.12 #if PRECISE_TIMING_MACH
651     semaphore_wait(wakeup_time_sem);
652     thread_suspend(timer_thread);
653     #endif
654 gbeauche 1.4 #if PRECISE_TIMING_POSIX
655     timer_thread_suspend();
656 gbeauche 1.6 pthread_mutex_lock(&wakeup_time_lock);
657 gbeauche 1.4 #endif
658     wakeup_time = wakeup_time_max;
659 cebix 1.1 for (int j=0; j<NUM_DESCS; j++) {
660     if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
661 gbeauche 1.4 if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
662 cebix 1.1 wakeup_time = desc[j].wakeup;
663     }
664 gbeauche 1.4 #if PRECISE_TIMING_BEOS
665 cebix 1.1 release_sem(wakeup_time_sem);
666     thread_info info;
667     do {
668     resume_thread(timer_thread); // This will unblock the thread
669     get_thread_info(timer_thread, &info);
670     } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
671     #endif
672 asvitkine 1.12 #if PRECISE_TIMING_MACH
673     semaphore_signal(wakeup_time_sem);
674     thread_abort(timer_thread);
675     thread_resume(timer_thread);
676     #endif
677 gbeauche 1.4 #if PRECISE_TIMING_POSIX
678 gbeauche 1.6 pthread_mutex_unlock(&wakeup_time_lock);
679 gbeauche 1.4 timer_thread_resume();
680     assert(suspend_count == 0);
681     #endif
682     #endif
683 cebix 1.1 }