Check for SYS/GL during library init. Reason is that
[AROS.git] / arch / m68k-amiga / timer / lowlevel.c
blobebdaf43fc4fe68d59a2340b25f391a292347d5b6
1 /*
2 Copyright © 1995-2010, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: CIA timer.device
6 Lang: english
7 */
8 #include <exec/types.h>
9 #include <exec/execbase.h>
10 #include <hardware/cia.h>
11 #include <proto/exec.h>
12 #include <proto/timer.h>
13 #include <proto/cia.h>
15 #include <timer_intern.h>
17 #define DEBUG 0
18 #include <aros/debug.h>
20 // convert timeval pair to 64-bit e-clock unit or 32-bit vblank
21 void convertunits(struct TimerBase *TimerBase, struct timeval *tr, int unit)
23 if (unit == UNIT_VBLANK) {
24 ULONG v = tr->tv_secs * TimerBase->tb_vblank_rate;
25 v += tr->tv_micro / TimerBase->tb_vblank_micros;
26 tr->tv_secs = 0;
27 tr->tv_micro = v;
28 } else if (unit == UNIT_MICROHZ) {
29 long long v = (long long)tr->tv_secs * TimerBase->tb_eclock_rate;
30 v += ((long long)tr->tv_micro * TimerBase->tb_micro_eclock_mult) >> 15;
31 tr->tv_secs = v >> 32;
32 tr->tv_micro = v;
36 // dst++
37 void inc64(struct timeval *dst)
39 dst->tv_micro++;
40 if (dst->tv_micro == 0)
41 dst->tv_secs++;
43 // dst += src
44 void add64(struct timeval *dst, struct timeval *src)
46 ULONG old = dst->tv_micro;
47 dst->tv_micro += src->tv_micro;
48 if (old > dst->tv_micro)
49 dst->tv_secs++;
50 dst->tv_secs += src->tv_secs;
52 // true if tv1 == tv2
53 BOOL equ64(struct timeval *tv1, struct timeval *tv2)
55 return tv1->tv_secs == tv2->tv_secs && tv1->tv_micro == tv2->tv_micro;
57 // return true if tv1 >= tv2
58 BOOL cmp64(struct timeval *tv1, struct timeval *tv2)
60 if (tv1->tv_secs > tv2->tv_secs)
61 return TRUE;
62 if (tv1->tv_secs == tv2->tv_secs && tv1->tv_micro >= tv2->tv_micro)
63 return TRUE;
64 return FALSE;
66 // return (larger - smaller) or 0xffffffff if result does not fit in ULONG
67 // larger >= smaller
68 ULONG sub64(struct timeval *larger, struct timeval *smaller)
70 if (larger->tv_secs == smaller->tv_secs)
71 return larger->tv_micro - smaller->tv_micro;
72 if (larger->tv_secs == smaller->tv_secs + 1) {
73 if (larger->tv_micro > smaller->tv_micro)
74 return 0xffffffff;
75 return (~smaller->tv_micro) + larger->tv_micro + 1;
77 return 0xffffffff;
80 void addmicro(struct TimerBase *TimerBase, struct timeval *tv)
82 ULONG old;
83 UBYTE lo, hi;
84 UWORD val;
86 add64(tv, &TimerBase->tb_micro_count);
88 if (!TimerBase->tb_micro_on)
89 return;
90 if (IsListEmpty(&TimerBase->tb_Lists[UNIT_MICROHZ])) {
91 TimerBase->tb_micro_on = FALSE;
92 return;
94 /* add (tb_micro_started - current counter value) */
95 for (;;) {
96 hi = *TimerBase->tb_micro_hi;
97 lo = *TimerBase->tb_micro_lo;
98 if (hi == *TimerBase->tb_micro_hi)
99 break;
101 val = (hi << 8) | lo;
103 if (val > TimerBase->tb_micro_started)
104 val = TimerBase->tb_micro_started;
105 else
106 val = TimerBase->tb_micro_started - val;
108 old = tv->tv_micro;
109 tv->tv_micro += val;
110 if (old > tv->tv_micro)
111 tv->tv_secs++;
114 // Disabled state assumed
115 void CheckTimer(struct TimerBase *TimerBase, UWORD unitnum)
117 if (unitnum == UNIT_VBLANK) {
118 TimerBase->tb_vblank_on = TRUE;
119 } else if (unitnum == UNIT_MICROHZ) {
120 if (!TimerBase->tb_micro_on) {
121 // not active, kickstart it
122 TimerBase->tb_micro_on = TRUE;
123 TimerBase->tb_micro_started = 0;
124 D(bug("ciaint_timer kickstarted\n"));
125 } else {
126 UBYTE lo, hi;
127 UWORD val;
128 // already active but new item was added to head
129 *TimerBase->tb_micro_cr &= 0x40;
130 *TimerBase->tb_micro_cr |= 0x08;
131 hi = *TimerBase->tb_micro_hi;
132 lo = *TimerBase->tb_micro_lo;
133 val = (hi << 8) | lo;
134 // how long have we already waited?
135 TimerBase->tb_micro_started -= val;
136 // force interrupt now
137 D(bug("ciaint_timer restarted\n"));
139 SetICR(TimerBase->tb_micro_res, 0x80 | (1 << TimerBase->tb_micro_intbit));
143 ULONG GetEClock(struct TimerBase *TimerBase)
145 UBYTE lo, hi;
146 ULONG diff, val;
148 /* Disable() assumed */
149 for (;;) {
150 hi = *TimerBase->tb_eclock_hi;
151 lo = *TimerBase->tb_eclock_lo;
152 if (hi == *TimerBase->tb_eclock_hi)
153 break;
154 // lo wraparound, try again
156 diff = 0;
157 // pending interrupt? Re-read counter */
158 if (SetICR(TimerBase->tb_eclock_res, 0) & (1 << TimerBase->tb_eclock_intbit)) {
159 diff = ECLOCK_BASE;
160 for (;;) {
161 hi = *TimerBase->tb_eclock_hi;
162 lo = *TimerBase->tb_eclock_lo;
163 if (hi == *TimerBase->tb_eclock_hi)
164 break;
167 val = (hi << 8) | lo;
168 diff += ECLOCK_BASE - val;
169 return diff;
172 AROS_INTH1(ciab_eclock, struct TimerBase *, TimerBase)
174 AROS_INTFUNC_INIT
176 D(bug("eclock int\n"));
178 // e-clock counter, counts full ECLOCK_BASE cycles
179 ULONG old = TimerBase->tb_eclock.ev_lo;
180 TimerBase->tb_eclock.ev_lo += ECLOCK_BASE;
181 if (old > TimerBase->tb_eclock.ev_lo)
182 TimerBase->tb_eclock.ev_hi++;
184 TimerBase->tb_eclock_to_usec += ECLOCK_BASE;
185 if (TimerBase->tb_eclock_to_usec >= TimerBase->tb_eclock_rate) {
186 TimerBase->tb_eclock_to_usec -= TimerBase->tb_eclock_rate;
187 TimerBase->tb_CurrentTime.tv_secs++;
190 return FALSE;
192 AROS_INTFUNC_EXIT
195 AROS_INTH1(ciaint_timer, struct TimerBase *, TimerBase)
197 AROS_INTFUNC_INIT
199 struct timerequest *tr, *next;
200 ULONG old;
202 D(bug("ciaint_timer\n"));
204 if (TimerBase->tb_micro_on == FALSE)
205 return 0;
207 // we have counted tb_micro_started since last interrupt
208 old = TimerBase->tb_micro_count.tv_micro;
209 TimerBase->tb_micro_count.tv_micro += TimerBase->tb_micro_started;
210 if (old > TimerBase->tb_micro_count.tv_micro)
211 TimerBase->tb_micro_count.tv_secs++;
213 Disable();
215 ForeachNodeSafe(&TimerBase->tb_Lists[UNIT_MICROHZ], tr, next) {
216 D(bug("%d/%d %d/%d\n", TimerBase->tb_micro_count.tv_secs, TimerBase->tb_micro_count.tv_micro, tr->tr_time.tv_secs, tr->tr_time.tv_micro));
217 if (cmp64(&TimerBase->tb_micro_count, &tr->tr_time)) {
218 Remove((struct Node *)tr);
219 tr->tr_time.tv_secs = tr->tr_time.tv_micro = 0;
220 tr->tr_node.io_Error = 0;
221 ReplyMsg((struct Message *)tr);
222 D(bug("ciaint_timer %x done\n", tr));
223 } else {
224 break; // first not finished, can stop searching
228 tr = (struct timerequest*)(((struct List *)(&TimerBase->tb_Lists[UNIT_MICROHZ]))->lh_Head);
229 if (tr->tr_node.io_Message.mn_Node.ln_Succ) {
230 ULONG newcount = sub64(&tr->tr_time, &TimerBase->tb_micro_count);
231 D(bug("ciaint_timer newcount=%d\n", newcount));
232 // longer than max CIA timer capacity?
233 if (newcount > 0xffff)
234 newcount = 0xffff;
235 TimerBase->tb_micro_started = newcount;
236 // reset control register, some badly behaving programs may have changed it
237 // do not touch bit 6 because it is used by keyboard handshake and reset warning
238 *TimerBase->tb_micro_cr &= 0x40;
239 *TimerBase->tb_micro_cr |= 0x08;
240 // reload new timer value (timer autostarts)
241 *TimerBase->tb_micro_lo = (UBYTE)(newcount >> 0);
242 *TimerBase->tb_micro_hi = (UBYTE)(newcount >> 8);
243 } else {
244 D(bug("ciaint_timer off\n"));
245 // list is empty
246 TimerBase->tb_micro_on = FALSE;
249 Enable();
251 return 0;
253 AROS_INTFUNC_EXIT
256 AROS_INTH1(cia_vbint, struct TimerBase *, TimerBase)
258 AROS_INTFUNC_INIT
260 struct timerequest *tr, *next;
262 if (TimerBase->tb_vblank_on == FALSE)
263 return 0;
264 inc64(&TimerBase->tb_vb_count);
266 Disable();
267 ForeachNodeSafe(&TimerBase->tb_Lists[UNIT_VBLANK], tr, next) {
268 if (cmp64(&TimerBase->tb_vb_count, &tr->tr_time)) {
269 Remove((struct Node *)tr);
270 tr->tr_time.tv_secs = tr->tr_time.tv_micro = 0;
271 tr->tr_node.io_Error = 0;
272 ReplyMsg((struct Message *)tr);
273 D(bug("vblank %x done\n", tr));
274 } else {
275 break; // first not finished, can stop searching
279 if (IsListEmpty(&TimerBase->tb_Lists[UNIT_VBLANK])) {
280 TimerBase->tb_vblank_on = FALSE;
282 Enable();
284 return 0;
286 AROS_INTFUNC_EXIT