ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/timer.cpp
Revision: 1.2
Committed: 2004-01-12T15:37:19Z (20 years, 10 months ago) by cebix
Branch: MAIN
Changes since 1.1: +1 -1 lines
Log Message:
Happy New Year! :)

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * timer.cpp - Time Manager emulation
3     *
4 cebix 1.2 * SheepShaver (C) 1997-2004 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     /*
22     * TODO: Prime(0)
23     */
24    
25     #include "sysdeps.h"
26     #include "timer.h"
27     #include "macos_util.h"
28     #include "main.h"
29     #include "cpu_emulation.h"
30    
31     #define DEBUG 0
32     #include "debug.h"
33    
34    
35     #if __BEOS__
36     #define PRECISE_TIMING 1
37     #else
38     #define PRECISE_TIMING 0
39     #endif
40    
41     #define TM_QUEUE 0 // Enable TMQueue management (doesn't work)
42    
43    
44     // Definitions for Time Manager
45     enum { // TMTask struct
46     tmAddr = 6,
47     tmCount = 10,
48     tmWakeUp = 14,
49     tmReserved = 18
50     };
51    
52    
53     // Array of additional info for each installed TMTask
54     struct TMDesc {
55     uint32 task; // Mac address of associated TMTask
56     tm_time_t wakeup; // Time this task is scheduled for execution
57     bool in_use; // Flag: descriptor in use
58     };
59    
60     const int NUM_DESCS = 64; // Maximum number of descriptors
61     static TMDesc desc[NUM_DESCS];
62    
63     #if PRECISE_TIMING
64     static thread_id timer_thread = -1;
65     static bool thread_active = true;
66     static volatile tm_time_t wakeup_time = 0x7fffffffffffffff;
67     static sem_id wakeup_time_sem = -1;
68     static int32 timer_func(void *arg);
69     #endif
70    
71    
72     /*
73     * Allocate descriptor for given TMTask in list
74     */
75    
76     static int alloc_desc(uint32 tm)
77     {
78     // Search for first free descriptor
79     for (int i=0; i<NUM_DESCS; i++)
80     if (!desc[i].in_use) {
81     desc[i].task = tm;
82     desc[i].in_use = true;
83     return i;
84     }
85     return -1;
86     }
87    
88    
89     /*
90     * Free descriptor in list
91     */
92    
93     inline static void free_desc(int i)
94     {
95     desc[i].in_use = false;
96     }
97    
98    
99     /*
100     * Find descriptor associated with given TMTask
101     */
102    
103     inline static int find_desc(uint32 tm)
104     {
105     for (int i=0; i<NUM_DESCS; i++)
106     if (desc[i].in_use && desc[i].task == tm)
107     return i;
108     return -1;
109     }
110    
111    
112     /*
113     * Enqueue task in Time Manager queue
114     */
115    
116     static void enqueue_tm(uint32 tm)
117     {
118     #if TM_QUEUE
119     uint32 tm_var = ReadMacInt32(0xb30);
120     WriteMacInt32(tm + qLink, ReadMacInt32(tm_var));
121     WriteMacInt32(tm_var, tm);
122     #endif
123     }
124    
125    
126     /*
127     * Remove task from Time Manager queue
128     */
129    
130     static void dequeue_tm(uint32 tm)
131     {
132     #if TM_QUEUE
133     uint32 p = ReadMacInt32(0xb30);
134     while (p) {
135     uint32 next = ReadMacInt32(p + qLink);
136     if (next == tm) {
137     WriteMacInt32(p + qLink, ReadMacInt32(next + qLink));
138     return;
139     }
140     }
141     #endif
142     }
143    
144    
145     /*
146     * Initialize Time Manager
147     */
148    
149     void TimerInit(void)
150     {
151     // Mark all descriptors as inactive
152     for (int i=0; i<NUM_DESCS; i++)
153     free_desc(i);
154    
155     #if PRECISE_TIMING
156     // Start timer thread
157     wakeup_time_sem = create_sem(1, "Wakeup Time");
158     timer_thread = spawn_thread(timer_func, "Time Manager", B_REAL_TIME_PRIORITY, NULL);
159     resume_thread(timer_thread);
160     #endif
161     }
162    
163    
164     /*
165     * Exit Time Manager
166     */
167    
168     void TimerExit(void)
169     {
170     #if PRECISE_TIMING
171     // Quit timer thread
172     if (timer_thread > 0) {
173     status_t l;
174     thread_active = false;
175     suspend_thread(timer_thread);
176     resume_thread(timer_thread);
177     wait_for_thread(timer_thread, &l);
178     delete_sem(wakeup_time_sem);
179     }
180     #endif
181     }
182    
183    
184     /*
185     * Emulator reset, remove all timer tasks
186     */
187    
188     void TimerReset(void)
189     {
190     // Mark all descriptors as inactive
191     for (int i=0; i<NUM_DESCS; i++)
192     free_desc(i);
193     }
194    
195    
196     /*
197     * Insert timer task
198     */
199    
200     int16 InsTime(uint32 tm, uint16 trap)
201     {
202     D(bug("InsTime %08lx, trap %04x\n", tm, trap));
203     WriteMacInt16((uint32)tm + qType, ReadMacInt16((uint32)tm + qType) & 0x1fff | (trap << 4) & 0x6000);
204     if (find_desc(tm) >= 0)
205     printf("WARNING: InsTime(): Task re-inserted\n");
206     else {
207     int i = alloc_desc(tm);
208     if (i < 0)
209     printf("FATAL: InsTime(): No free Time Manager descriptor\n");
210     }
211     return 0;
212     }
213    
214    
215     /*
216     * Remove timer task
217     */
218    
219     int16 RmvTime(uint32 tm)
220     {
221     D(bug("RmvTime %08lx\n", tm));
222    
223     // Find descriptor
224     int i = find_desc(tm);
225     if (i < 0) {
226     printf("WARNING: RmvTime(%08lx): Descriptor not found\n", tm);
227     return 0;
228     }
229    
230     // Task active?
231     #if PRECISE_TIMING
232     while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
233     suspend_thread(timer_thread);
234     #endif
235     if (ReadMacInt16(tm + qType) & 0x8000) {
236    
237     // Yes, make task inactive and remove it from the Time Manager queue
238     WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) & 0x7fff);
239     dequeue_tm(tm);
240     #if PRECISE_TIMING
241     // Look for next task to be called and set wakeup_time
242     wakeup_time = 0x7fffffffffffffff;
243     for (int j=0; j<NUM_DESCS; j++) {
244     if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
245     if (desc[j].wakeup < wakeup_time)
246     wakeup_time = desc[j].wakeup;
247     }
248     #endif
249    
250     // Compute remaining time
251     tm_time_t remaining, current;
252     timer_current_time(current);
253     timer_sub_time(remaining, desc[i].wakeup, current);
254     WriteMacInt32(tm + tmCount, timer_host2mac_time(remaining));
255     } else
256     WriteMacInt32(tm + tmCount, 0);
257     D(bug(" tmCount %ld\n", ReadMacInt32(tm + tmCount)));
258     #if PRECISE_TIMING
259     release_sem(wakeup_time_sem);
260     thread_info info;
261     do {
262     resume_thread(timer_thread); // This will unblock the thread
263     get_thread_info(timer_thread, &info);
264     } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
265     #endif
266    
267     // Free descriptor
268     free_desc(i);
269     return 0;
270     }
271    
272    
273     /*
274     * Start timer task
275     */
276    
277     int16 PrimeTime(uint32 tm, int32 time)
278     {
279     D(bug("PrimeTime %08lx, time %ld\n", tm, time));
280    
281     // Find descriptor
282     int i = find_desc(tm);
283     if (i < 0) {
284     printf("FATAL: PrimeTime(): Descriptor not found\n");
285     return 0;
286     }
287    
288     // Convert delay time
289     tm_time_t delay;
290     timer_mac2host_time(delay, time);
291    
292     // Extended task?
293     if (ReadMacInt16(tm + qType) & 0x4000) {
294    
295     // Yes, tmWakeUp set?
296     if (ReadMacInt32(tm + tmWakeUp)) {
297    
298     //!! PrimeTime(0) means continue previous delay
299     // (save wakeup time in RmvTime?)
300     if (time == 0) {
301     printf("FATAL: Unsupported PrimeTime(0)\n");
302     return 0;
303     }
304    
305     // Yes, calculate wakeup time relative to last scheduled time
306     tm_time_t wakeup;
307     timer_add_time(wakeup, desc[i].wakeup, delay);
308     desc[i].wakeup = wakeup;
309    
310     } else {
311    
312     // No, calculate wakeup time relative to current time
313     tm_time_t now;
314     timer_current_time(now);
315     timer_add_time(desc[i].wakeup, now, delay);
316     }
317    
318     // Set tmWakeUp to indicate that task was scheduled
319     WriteMacInt32(tm + tmWakeUp, 0x12345678);
320    
321     } else {
322    
323     // Not extended task, calculate wakeup time relative to current time
324     tm_time_t now;
325     timer_current_time(now);
326     timer_add_time(desc[i].wakeup, now, delay);
327     }
328    
329     // Make task active and enqueue it in the Time Manager queue
330     #if PRECISE_TIMING
331     while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
332     suspend_thread(timer_thread);
333     #endif
334     WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) | 0x8000);
335     enqueue_tm(tm);
336     #if PRECISE_TIMING
337     // Look for next task to be called and set wakeup_time
338     wakeup_time = 0x7fffffffffffffff;
339     for (int j=0; j<NUM_DESCS; j++) {
340     if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
341     if (desc[j].wakeup < wakeup_time)
342     wakeup_time = desc[j].wakeup;
343     }
344     release_sem(wakeup_time_sem);
345     thread_info info;
346     do {
347     resume_thread(timer_thread); // This will unblock the thread
348     get_thread_info(timer_thread, &info);
349     } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
350     #endif
351     return 0;
352     }
353    
354    
355     #if PRECISE_TIMING
356     /*
357     * Time Manager thread
358     */
359    
360     static int32 timer_func(void *arg)
361     {
362     while (thread_active) {
363    
364     // Wait until time specified by wakeup_time
365     snooze_until(wakeup_time, B_SYSTEM_TIMEBASE);
366    
367     while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
368     if (wakeup_time < system_time()) {
369    
370     // Timer expired, trigger interrupt
371     wakeup_time = 0x7fffffffffffffff;
372     SetInterruptFlag(INTFLAG_TIMER);
373     TriggerInterrupt();
374     }
375     release_sem(wakeup_time_sem);
376     }
377     return 0;
378     }
379     #endif
380    
381    
382     /*
383     * Timer interrupt function (executed as part of 60Hz interrupt)
384     */
385    
386     void TimerInterrupt(void)
387     {
388     // D(bug("TimerIRQ\n"));
389    
390     // Look for active TMTasks that have expired
391     tm_time_t now;
392     timer_current_time(now);
393     for (int i=0; i<NUM_DESCS; i++)
394     if (desc[i].in_use) {
395     uint32 tm = desc[i].task;
396     if ((ReadMacInt16(tm + qType) & 0x8000) && timer_cmp_time(desc[i].wakeup, now) <= 0) {
397    
398     // Found one, mark as inactive and remove it from the Time Manager queue
399     WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) & 0x7fff);
400     dequeue_tm(tm);
401    
402     // Call timer function
403     uint32 addr = ReadMacInt32(tm + tmAddr);
404     if (addr) {
405     D(bug("Calling TimeTask %08lx, addr %08lx\n", tm, addr));
406     M68kRegisters r;
407     r.a[0] = addr;
408     r.a[1] = tm;
409     Execute68k(r.a[0], &r);
410     D(bug(" returned from TimeTask\n"));
411     }
412     }
413     }
414    
415     #if PRECISE_TIMING
416     // Look for next task to be called and set wakeup_time
417     while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
418     suspend_thread(timer_thread);
419     wakeup_time = 0x7fffffffffffffff;
420     for (int j=0; j<NUM_DESCS; j++) {
421     if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
422     if (desc[j].wakeup < wakeup_time)
423     wakeup_time = desc[j].wakeup;
424     }
425     release_sem(wakeup_time_sem);
426     thread_info info;
427     do {
428     resume_thread(timer_thread); // This will unblock the thread
429     get_thread_info(timer_thread, &info);
430     } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
431     #endif
432     }