revert between 56095 -> 55830 in arch
[AROS.git] / arch / m68k-amiga / timer / lowlevel.c
bloba3b5f036c8d8e41c15ac48c051b2e9be46c13133
1 /*
2 Copyright © 1995-2018, 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++;
188 TimerBase->tb_Elapsed.tv_secs++;
191 return FALSE;
193 AROS_INTFUNC_EXIT
196 AROS_INTH1(ciaint_timer, struct TimerBase *, TimerBase)
198 AROS_INTFUNC_INIT
200 struct timerequest *tr, *next;
201 ULONG old;
203 D(bug("ciaint_timer\n"));
205 if (TimerBase->tb_micro_on == FALSE)
206 return 0;
208 // we have counted tb_micro_started since last interrupt
209 old = TimerBase->tb_micro_count.tv_micro;
210 TimerBase->tb_micro_count.tv_micro += TimerBase->tb_micro_started;
211 if (old > TimerBase->tb_micro_count.tv_micro)
212 TimerBase->tb_micro_count.tv_secs++;
214 Disable();
216 ForeachNodeSafe(&TimerBase->tb_Lists[UNIT_MICROHZ], tr, next) {
217 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));
218 if (cmp64(&TimerBase->tb_micro_count, &tr->tr_time)) {
219 Remove((struct Node *)tr);
220 tr->tr_time.tv_secs = tr->tr_time.tv_micro = 0;
221 tr->tr_node.io_Error = 0;
222 ReplyMsg((struct Message *)tr);
223 D(bug("ciaint_timer %x done\n", tr));
224 } else {
225 break; // first not finished, can stop searching
229 tr = (struct timerequest*)(((struct List *)(&TimerBase->tb_Lists[UNIT_MICROHZ]))->lh_Head);
230 if (tr->tr_node.io_Message.mn_Node.ln_Succ) {
231 ULONG newcount = sub64(&tr->tr_time, &TimerBase->tb_micro_count);
232 D(bug("ciaint_timer newcount=%d\n", newcount));
233 // longer than max CIA timer capacity?
234 if (newcount > 0xffff)
235 newcount = 0xffff;
236 TimerBase->tb_micro_started = newcount;
237 // reset control register, some badly behaving programs may have changed it
238 // do not touch bit 6 because it is used by keyboard handshake and reset warning
239 *TimerBase->tb_micro_cr &= 0x40;
240 *TimerBase->tb_micro_cr |= 0x08;
241 // reload new timer value (timer autostarts)
242 *TimerBase->tb_micro_lo = (UBYTE)(newcount >> 0);
243 *TimerBase->tb_micro_hi = (UBYTE)(newcount >> 8);
244 } else {
245 D(bug("ciaint_timer off\n"));
246 // list is empty
247 TimerBase->tb_micro_on = FALSE;
250 Enable();
252 return 0;
254 AROS_INTFUNC_EXIT
257 AROS_INTH1(cia_vbint, struct TimerBase *, TimerBase)
259 AROS_INTFUNC_INIT
261 struct timerequest *tr, *next;
263 if (TimerBase->tb_vblank_on == FALSE)
264 return 0;
265 inc64(&TimerBase->tb_vb_count);
267 Disable();
268 ForeachNodeSafe(&TimerBase->tb_Lists[UNIT_VBLANK], tr, next) {
269 if (cmp64(&TimerBase->tb_vb_count, &tr->tr_time)) {
270 Remove((struct Node *)tr);
271 tr->tr_time.tv_secs = tr->tr_time.tv_micro = 0;
272 tr->tr_node.io_Error = 0;
273 ReplyMsg((struct Message *)tr);
274 D(bug("vblank %x done\n", tr));
275 } else {
276 break; // first not finished, can stop searching
280 if (IsListEmpty(&TimerBase->tb_Lists[UNIT_VBLANK])) {
281 TimerBase->tb_vblank_on = FALSE;
283 Enable();
285 return 0;
287 AROS_INTFUNC_EXIT