Do not touch CIA-A control register A bit 6. (Keyboard handshake bit, reset warning...
[AROS.git] / arch / m68k-amiga / timer / lowlevel.c
blob336265ccb1c63020b189d6bfa6788eeadb44d12e
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_UFH4(APTR, ciab_eclock,
173 AROS_UFHA(ULONG, dummy, A0),
174 AROS_UFHA(struct TimerBase *, TimerBase, A1),
175 AROS_UFHA(ULONG, dummy2, A5),
176 AROS_UFHA(struct ExecBase *, SysBase, A6))
178 AROS_USERFUNC_INIT
180 D(bug("eclock int\n"));
182 // e-clock counter, counts full ECLOCK_BASE cycles
183 ULONG old = TimerBase->tb_eclock.ev_lo;
184 TimerBase->tb_eclock.ev_lo += ECLOCK_BASE;
185 if (old > TimerBase->tb_eclock.ev_lo)
186 TimerBase->tb_eclock.ev_hi++;
188 TimerBase->tb_eclock_to_usec += ECLOCK_BASE;
189 if (TimerBase->tb_eclock_to_usec >= TimerBase->tb_eclock_rate) {
190 TimerBase->tb_eclock_to_usec -= TimerBase->tb_eclock_rate;
191 TimerBase->tb_CurrentTime.tv_secs++;
194 return 0;
196 AROS_USERFUNC_EXIT
199 AROS_UFH4(APTR, ciaint_timer,
200 AROS_UFHA(ULONG, dummy, A0),
201 AROS_UFHA(struct TimerBase *, TimerBase, A1),
202 AROS_UFHA(ULONG, dummy2, A5),
203 AROS_UFHA(struct ExecBase *, SysBase, A6))
205 AROS_USERFUNC_INIT
207 struct timerequest *tr, *next;
208 ULONG old;
210 D(bug("ciaint_timer\n"));
212 if (TimerBase->tb_micro_on == FALSE)
213 return 0;
215 // we have counted tb_micro_started since last interrupt
216 old = TimerBase->tb_micro_count.tv_micro;
217 TimerBase->tb_micro_count.tv_micro += TimerBase->tb_micro_started;
218 if (old > TimerBase->tb_micro_count.tv_micro)
219 TimerBase->tb_micro_count.tv_secs++;
221 Disable();
223 ForeachNodeSafe(&TimerBase->tb_Lists[UNIT_MICROHZ], tr, next) {
224 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));
225 if (cmp64(&TimerBase->tb_micro_count, &tr->tr_time)) {
226 Remove((struct Node *)tr);
227 tr->tr_time.tv_secs = tr->tr_time.tv_micro = 0;
228 tr->tr_node.io_Error = 0;
229 ReplyMsg((struct Message *)tr);
230 D(bug("ciaint_timer %x done\n", tr));
231 } else {
232 break; // first not finished, can stop searching
236 tr = (struct timerequest*)(((struct List *)(&TimerBase->tb_Lists[UNIT_MICROHZ]))->lh_Head);
237 if (tr->tr_node.io_Message.mn_Node.ln_Succ) {
238 ULONG newcount = sub64(&tr->tr_time, &TimerBase->tb_micro_count);
239 D(bug("ciaint_timer newcount=%d\n", newcount));
240 // longer than max CIA timer capacity?
241 if (newcount > 0xffff)
242 newcount = 0xffff;
243 TimerBase->tb_micro_started = newcount;
244 // reset control register, some badly behaving programs may have changed it
245 // do not touch bit 6 because it is used by keyboard handshake and reset warning
246 *TimerBase->tb_micro_cr &= 0x40;
247 *TimerBase->tb_micro_cr |= 0x08;
248 // reload new timer value (timer autostarts)
249 *TimerBase->tb_micro_lo = (UBYTE)(newcount >> 0);
250 *TimerBase->tb_micro_hi = (UBYTE)(newcount >> 8);
251 } else {
252 D(bug("ciaint_timer off\n"));
253 // list is empty
254 TimerBase->tb_micro_on = FALSE;
257 Enable();
259 return 0;
261 AROS_USERFUNC_EXIT
265 AROS_UFH4(APTR, cia_vbint,
266 AROS_UFHA(ULONG, dummy, A0),
267 AROS_UFHA(struct TimerBase *, TimerBase, A1),
268 AROS_UFHA(ULONG, dummy2, A5),
269 AROS_UFHA(struct ExecBase *, SysBase, A6))
271 AROS_USERFUNC_INIT
273 struct timerequest *tr, *next;
275 if (TimerBase->tb_vblank_on == FALSE)
276 return 0;
277 inc64(&TimerBase->tb_vb_count);
279 Disable();
280 ForeachNodeSafe(&TimerBase->tb_Lists[UNIT_VBLANK], tr, next) {
281 if (cmp64(&TimerBase->tb_vb_count, &tr->tr_time)) {
282 Remove((struct Node *)tr);
283 tr->tr_time.tv_secs = tr->tr_time.tv_micro = 0;
284 tr->tr_node.io_Error = 0;
285 ReplyMsg((struct Message *)tr);
286 D(bug("vblank %x done\n", tr));
287 } else {
288 break; // first not finished, can stop searching
292 if (IsListEmpty(&TimerBase->tb_Lists[UNIT_VBLANK])) {
293 TimerBase->tb_vblank_on = FALSE;
295 Enable();
297 return 0;
299 AROS_USERFUNC_EXIT