2 Copyright © 1995-2018, The AROS Development Team. All rights reserved.
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>
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
;
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;
37 void inc64(struct timeval
*dst
)
40 if (dst
->tv_micro
== 0)
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
)
50 dst
->tv_secs
+= src
->tv_secs
;
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
)
62 if (tv1
->tv_secs
== tv2
->tv_secs
&& tv1
->tv_micro
>= tv2
->tv_micro
)
66 // return (larger - smaller) or 0xffffffff if result does not fit in ULONG
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
)
75 return (~smaller
->tv_micro
) + larger
->tv_micro
+ 1;
80 void addmicro(struct TimerBase
*TimerBase
, struct timeval
*tv
)
86 add64(tv
, &TimerBase
->tb_micro_count
);
88 if (!TimerBase
->tb_micro_on
)
90 if (IsListEmpty(&TimerBase
->tb_Lists
[UNIT_MICROHZ
])) {
91 TimerBase
->tb_micro_on
= FALSE
;
94 /* add (tb_micro_started - current counter value) */
96 hi
= *TimerBase
->tb_micro_hi
;
97 lo
= *TimerBase
->tb_micro_lo
;
98 if (hi
== *TimerBase
->tb_micro_hi
)
101 val
= (hi
<< 8) | lo
;
103 if (val
> TimerBase
->tb_micro_started
)
104 val
= TimerBase
->tb_micro_started
;
106 val
= TimerBase
->tb_micro_started
- val
;
110 if (old
> tv
->tv_micro
)
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"));
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
)
148 /* Disable() assumed */
150 hi
= *TimerBase
->tb_eclock_hi
;
151 lo
= *TimerBase
->tb_eclock_lo
;
152 if (hi
== *TimerBase
->tb_eclock_hi
)
154 // lo wraparound, try again
157 // pending interrupt? Re-read counter */
158 if (SetICR(TimerBase
->tb_eclock_res
, 0) & (1 << TimerBase
->tb_eclock_intbit
)) {
161 hi
= *TimerBase
->tb_eclock_hi
;
162 lo
= *TimerBase
->tb_eclock_lo
;
163 if (hi
== *TimerBase
->tb_eclock_hi
)
167 val
= (hi
<< 8) | lo
;
168 diff
+= ECLOCK_BASE
- val
;
172 AROS_INTH1(ciab_eclock
, struct TimerBase
*, TimerBase
)
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
++;
196 AROS_INTH1(ciaint_timer
, struct TimerBase
*, TimerBase
)
200 struct timerequest
*tr
, *next
;
203 D(bug("ciaint_timer\n"));
205 if (TimerBase
->tb_micro_on
== FALSE
)
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
++;
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
));
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)
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);
245 D(bug("ciaint_timer off\n"));
247 TimerBase
->tb_micro_on
= FALSE
;
257 AROS_INTH1(cia_vbint
, struct TimerBase
*, TimerBase
)
261 struct timerequest
*tr
, *next
;
263 if (TimerBase
->tb_vblank_on
== FALSE
)
265 inc64(&TimerBase
->tb_vb_count
);
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
));
276 break; // first not finished, can stop searching
280 if (IsListEmpty(&TimerBase
->tb_Lists
[UNIT_VBLANK
])) {
281 TimerBase
->tb_vblank_on
= FALSE
;