Initial revision
[ssr] / StraySrc / Libraries / Sapphire / s / idle
1 ;
2 ; idle.s
3 ;
4 ; Idle event and alarm handling (TMA)
5 ;
6 ; © 1994-1998 Straylight
7 ;
8
9 ;----- Licensing note -------------------------------------------------------
10 ;
11 ; This file is part of Straylight's Sapphire library.
12 ;
13 ; Sapphire is free software; you can redistribute it and/or modify
14 ; it under the terms of the GNU General Public License as published by
15 ; the Free Software Foundation; either version 2, or (at your option)
16 ; any later version.
17 ;
18 ; Sapphire is distributed in the hope that it will be useful,
19 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ; GNU General Public License for more details.
22 ;
23 ; You should have received a copy of the GNU General Public License
24 ; along with Sapphire. If not, write to the Free Software Foundation,
25 ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
27 ;----- Standard header ------------------------------------------------------
28
29 GET libs:header
30 GET libs:swis
31
32 ;----- External dependencies ------------------------------------------------
33
34 GET sapphire:sapphire
35 GET sapphire:suballoc
36 GET sapphire:event
37
38 ;----- Main code ------------------------------------------------------------
39
40 AREA |Sapphire$$Code|,CODE,READONLY
41
42 ; --- idle__addToList ---
43 ;
44 ; On entry: R0 == Time word
45 ; R1 == pointer to routine to call
46 ; R2 == R10 value to call routine
47 ; R3 == R12 value to call routine with
48 ; R4 == pointer to list head to use
49 ;
50 ; On exit: R0-R4 preserved
51 ;
52 ; Use: Adds a rountine to the given list. Later added
53 ; routines are called first
54
55 idle__addToList ROUT
56
57 STMFD R13!,{R0-R4,R14} ;Stack some registers
58
59 ; --- Allocate a block ---
60
61 MOV R0,#list__size ;Size to allocate
62 BL sub_alloc ;Allocate the block
63 BVS %01 ;Branch ahead if error
64
65 ; --- Fill the block in ---
66
67 LDR R1,[R4] ;Get the list head
68 STR R1,[R0,#list__next] ;Store in next field
69 STR R0,[R4] ;Store new block at head
70
71 LDMIA R13,{R1-R4} ;Get parameters
72 STMIB R0,{R1-R4} ;Store them in the block
73
74 ; --- And return to user ---
75
76 LDMFD R13!,{R0-R4,R14} ;Load back link
77 BICS PC,R14,#V_flag ;Return without error
78
79 ; --- Barf if an error occured ---
80
81 01 ADD R13,R13,#4 ;Skip over R0,R1,R2
82 LDMFD R13!,{R1-R4,R14} ;Branch if error
83 ORRS PC,R14,#V_flag ;Return with error
84
85 LTORG
86
87 ; --- idle__removeFromList ---
88 ;
89 ; On entry: R0 == Time word
90 ; R1 == pointer to routine to be called
91 ; R2 == R10 value routine is called with
92 ; R3 == R12 value routine is called with
93 ; R4 == pointer to list head to use
94 ;
95 ; On exit: All registers/flags preserved
96 ;
97 ; Use: Removes a routine from the given list. All values are
98 ; compared.
99
100 idle__removeFromList
101 ROUT
102
103 STMFD R13!,{R0-R10,R12,R14}
104
105 ; --- Find the block ---
106
107 MOV R5,#0 ;The previous pointer
108 MOV R12,R4 ;Remember where the head is
109 LDR R4,[R4] ;Get the head of the list
110 01 TEQ R4,#0 ;Are we at the end?
111 LDMEQFD R13!,{R0-R10,R12,PC}^ ;Yes -- return
112 LDR R10,[R4],#4 ;Get the next pointer
113 LDMIA R4,{R6-R9} ;Load data from the block
114 CMP R6,R0 ;Are routines the same?
115 CMPEQ R7,R1 ;Yes -- and window/R4 handle?
116 CMPEQ R8,R2 ;Yes -- R10 value?
117 CMPEQ R9,R3 ;Yes -- R12 value?
118 SUBNE R5,R4,#4 ;If no, remember previous
119 MOVNE R4,R10 ;...get list pointer
120 BNE %01 ;...and keep looking
121
122 ; --- So now the block has been found ---
123
124 SUB R0,R4,#4 ;Put the block in R0
125 MOV R1,#list__size ;Get the size
126 BL sub_free ;Free the block
127 CMP R5,#0 ;Was there a previous block
128 STREQ R10,[R12,#0] ;No -- store next blk in head
129 STRNE R10,[R5,#0] ;Yes -- store in prev next ^
130
131 ; --- And return to the user ---
132
133 LDMFD R13!,{R0-R10,R12,PC}^
134
135 LTORG
136
137 ; --- idle_handler ---
138 ;
139 ; On entry: R0 == how frequently to call
140 ; R1 == pointer to routine to call
141 ; R2 == R10 value to call routine with
142 ; R3 == R12 value to call routine with
143 ;
144 ; On exit: May return an error
145 ;
146 ; Use: Adds a routine to the idle handler list. Later added
147 ; routines are called first. The idle handing routine
148 ; may corrupt R10 and R12.
149 ; A handler is only addeded if an identical one does not
150 ; already exist.
151
152 EXPORT idle_handler
153 idle_handler ROUT
154
155 BIC R14,R14,#V_flag ;Assume it's all OK
156 STMFD R13!,{R4-R9,R14} ;Save some registers
157 WSPACE idle__wSpace,R9 ;Get my workspace pointer
158
159 ; --- Check through the list ---
160
161 LDR R4,idle__iHandlers ;Get my idle handlers list
162 10idle_handler TEQ R4,#0 ;Are we at the end
163 BEQ %20idle_handler ;Yes -- jump ahead
164 LDMIA R4,{R4-R8} ;Load parameters to pass
165 CMP R5,R0 ;Is this identical?
166 CMPEQ R6,R1
167 CMPEQ R7,R2
168 CMPEQ R8,R3
169 BEQ %90idle_handler ;Yes -- return now then
170 B %10idle_handler ;Try next handler
171
172 ; --- Be careful not to alter flags ---
173
174 20idle_handler ADR R4,idle__iHandlers ;Get the idle handlers
175 BL idle__addToList ;Add the routine to the list
176 LDMVSFD R13!,{R4-R9,PC} ;Return if it failed
177
178 ; --- Cache the minimum return time ---
179
180 LDR R4,idle__minTime ;Get the current minimum time
181 CMP R4,#-1 ;Is there one?
182 STREQ R0,idle__minTime ;No -- store the new one
183 LDMEQFD R13!,{R4-R9,PC}^ ;...and return
184 CMP R0,R4 ;Is the new time lower
185 STRLE R0,idle__minTime ;Yes -- Store the new one
186
187 ; --- Return to client ---
188
189 90idle_handler LDMFD R13!,{R4-R9,PC}^ ;Return cunningly
190
191 ; --- idle_removeHandler ---
192 ;
193 ; On entry: R0 == How frequently it was called
194 ; R1 == pointer to routine called
195 ; R2 == R10 value routine is called with
196 ; R3 == R12 value routine is called with
197 ;
198 ; On exit: --
199 ;
200 ; Use: Removes a routine from the idle handler list.
201
202 EXPORT idle_removeHandler
203 idle_removeHandler
204 ROUT
205
206 STMFD R13!,{R4-R6,R9,R14} ;Stack some registers
207
208 ; --- Free the handler ---
209
210 WSPACE idle__wSpace,R9 ;Get my workspace pointer
211 ADR R4,idle__iHandlers ;Get the idle handlers
212 BL idle__removeFromList ;Remove routine from the list
213
214 ; --- Find the new minimum frequency ---
215
216 MOV R5,#-1 ;Lowest time found
217 LDR R4,[R4] ;Get the first handler
218 00 TEQ R4,#0 ;Is it the end?
219 BEQ %01 ;Yes -- finish
220 LDR R6,[R4,#list__time] ;Load the frequency
221 CMP R6,R5 ;Is it lower than lowest
222 MOVLO R5,R6 ;Yes -- remember it
223 LDR R4,[R4] ;Get next in list
224 B %00 ;Try the rest
225
226 01 STR R5,idle__minTime ;Store it found value
227 LDMFD R13!,{R4-R6,R9,R14} ;Load registers
228 BICS PC,R14,#V_flag ;Return without error
229
230 ; --- idle_setAlarm ---
231 ;
232 ; On entry: R3 == Time to call
233 ; R1 == pointer to routine to call
234 ; R2 == R10 value to call routine with
235 ; R3 == R12 value to call routine with
236 ;
237 ; On exit: May return an error
238 ;
239 ; Use: Adds a alarm to be called. The idle handing routine
240 ; may corrupt R10 and R12.
241
242 EXPORT idle_setAlarm
243 idle_setAlarm ROUT
244
245 STMFD R13!,{R0-R4,R9,R14} ;Stack some registers
246 WSPACE idle__wSpace,R9 ;Get my workspace
247 MOV R1,R0 ;Put requested time in R1
248
249 ; --- Allocate a block ---
250
251 MOV R0,#list__size ;Size to allocate
252 BL sub_alloc ;Allocate the block
253 BVS %02idle_setAlarm ;Branch ahead if error
254
255 ; --- Work out where to put it ---
256
257 MOV R2,#0 ;Previous alarm
258 LDR R4,idle__aHandlers ;Get the list head
259 00idle_setAlarm CMP R4,#0 ;Are we at the end
260 BEQ %01idle_setAlarm ;Put it in here
261 LDR R3,[R4,#4] ;Get the time due
262 CMP R3,R1 ;Compare with new alarm
263 BGT %01idle_setAlarm ;If bigger, put it here
264 MOV R2,R4 ;Remember this alarm
265 LDR R4,[R4,#0] ;Get next alarm in list
266 B %00idle_setAlarm
267
268 ; --- Put the alarm in between R2 and R4 ---
269
270 01idle_setAlarm STR R4,[R0,#list__next] ;Store in next field
271 CMP R2,#0 ;Was there a previous alarm
272 STREQ R0,idle__aHandlers ;No, store new block at head
273 STRNE R0,[R2,#0] ;Yes, store in its next field
274
275 ; --- Fill the block in ---
276
277 LDMIA R13,{R1-R4} ;Get parameters
278 STMIB R0,{R1-R4} ;Store them in the block
279
280 ; --- And return to user ---
281
282 LDMFD R13!,{R0-R4,R9,R14} ;Load back link
283 BICS PC,R14,#V_flag ;Return without error
284
285 ; --- Barf if an error occured ---
286
287 02idle_setAlarm ADD R13,R13,#4 ;Skip over R0
288 LDMFD R13!,{R1-R4,R9,R14} ;Branch if error
289 ORRS PC,R14,#V_flag ;Return with error
290
291 LTORG
292
293
294 ; --- idle_removeAlarm ---
295 ;
296 ; On entry: R0 == When it was to be called
297 ; R1 == pointer to routine called
298 ; R2 == R10 value routine is called with
299 ; R3 == R12 value routine is called with
300 ;
301 ; On exit: --
302 ;
303 ; Use: Removes a routine from the idle handler list. It has
304 ; no effect if it doesn't exist.
305
306 EXPORT idle_removeAlarm
307 idle_removeAlarm
308 ROUT
309
310 STMFD R13!,{R4-R6,R9,R14} ;Stack some registers
311
312 ; --- Free the handler ---
313
314 WSPACE idle__wSpace,R9 ;Get my workspace pointer
315 ADR R4,idle__aHandlers ;Get the idle handlers
316 BL idle__removeFromList ;Remove routine from the list
317
318 ; --- And return ---
319
320 LDMFD R13!,{R4-R6,R9,R14} ;Load registers
321 BICS PC,R14,#V_flag ;Return without error
322
323 ; --- idle_removeAllAlarms ---
324 ;
325 ; On entry: R0 == R10 value to look for
326 ;
327 ; On exit: --
328 ;
329 ; Use: Removes all alarms with the handle that was passed to them
330 ; to be put into R10. You should not remove an alarm within
331 ; an alarm handler.
332
333
334 EXPORT idle_removeAllAlarms
335 idle_removeAllAlarms
336 ROUT
337
338 STMFD R13!,{R0-R10,R14} ;Stack some registers
339 WSPACE idle__wSpace,R9 ;Get the alarm handler list
340 MOV R10,R0 ;Remember handle
341
342 ; --- Find the first block with the handle block ---
343
344 MOV R2,#0 ;The previous pointer
345 LDR R3,idle__aHandlers ;Get the head of the list
346 00 TEQ R3,#0 ;Are we at the end?
347 LDMEQFD R13!,{R0-R10,PC}^ ;Yes -- return
348 LDR R4,[R3],#4 ;Get the next pointer
349 LDMIA R3,{R5-R8} ;Load data from the block
350 CMP R7,R10 ;Are handles the same?
351 SUBNE R2,R3,#4 ;If no, remember previous
352 MOVNE R3,R4 ;...point to the next block
353 BNE %00 ;...and keep looking
354
355 ; --- So now the block has been found ---
356
357 SUB R0,R3,#4 ;Put the block in R0
358 MOV R1,#list__size ;Get the size
359 BL sub_free ;Free the block
360 CMP R2,#0 ;Was there a previous block
361 STREQ R4,idle__aHandlers ;No -- store next blk in head
362 STRNE R4,[R2,#0] ;Yes -- store in prev next ^
363 MOV R3,R4 ;Point at the next block
364 B %00 ;Find more alarms
365
366 LTORG
367
368 ; --- idle__preFilter ---
369 ;
370 ; On entry: R0 == reason code return from Wimp_Poll
371 ; R1 == pointer to block
372 ; R2 == earliest time to return with NULL
373 ;
374 ; On exit: R2 altered as appropriate
375
376 idle__preFilter ROUT
377
378 STMFD R13!,{R0,R5-R7,R9,R14}
379 MOV R9,R12
380
381 ; --- Is the user requesting idles? ---
382
383 TST R0,#1 ;Is it masked off
384 CMPEQ R2,#0 ;If no, just Wimp_Poll?
385 LDMEQFD R13!,{R0,R5-R7,R9,PC}^ ;Return PDQ
386
387 ; --- Get the minimum time to return with ---
388
389 LDR R5,idle__iHandlers ;Get the idle handlers list
390 LDR R6,idle__aHandlers ;Get the alarm handlers list
391 CMP R5,#0 ;Do we have idle handlers?
392 CMPEQ R6,#0 ;If not, any alarm handlers?
393 LDMEQFD R13!,{R0,R5-R7,R9,PC}^ ;No idles/alarms -- return
394
395 ; --- Get the next time the quickest idle will call ---
396
397 LDR R7,idle__minTime ;Get the minimum frequency
398 CMP R7,#-1 ;Is there one?
399 SWINE OS_ReadMonotonicTime ;Yes -- get the current time
400 ADDNE R5,R7,R0 ;...and time to return with
401
402 ; --- And the time of the next alarm ---
403
404 CMP R6,#0 ;Are there any alarms?
405 BEQ %00 ;No -- R5 contains quickest
406 LDR R6,[R6,#list__time] ;Get time of the next alarm
407 CMP R7,#-1 ;Was there an idle handler?
408 MOVEQ R5,R6 ;No, R6 is lowest
409 BEQ %00 ;...branch ahead
410 CMP R6,R5 ;Is it before next idle
411 MOVLT R5,R6 ;Yes, use this time
412
413 ; --- Now set up the mask word and return time ---
414
415 00 LDR R0,[R13],#4 ;Get event mask back
416 TST R0,#1 ;Is the user expecting idles
417 BEQ %01 ;Yes -- Do a comparison
418 BIC R0,R0,#1 ;Clear the mask bit
419 MOV R2,R5 ;Put the minimum time in R2
420 LDMFD R13!,{R5-R7,R9,PC}^ ;...and return
421
422 ; --- The user is expecting idles, so please him ---
423
424 01 CMP R5,R2 ;Which is bigger?
425 MOVLT R2,R5 ;Swap if needed
426 LDMFD R13!,{R5-R7,R9,PC}^ ;...and return
427
428 ; --- idle__dispatch ---
429 ;
430 ; On entry: R0 == reason code returned from Wimp_Poll
431 ; R1 == pointer to block
432 ; R12 == pointer to workspace
433 ;
434 ; On exit: --
435
436 idle__dispatch ROUT
437
438 CMP R0,#0 ;Is it a NULL event
439 MOVNES PC,R14 ;No - return
440 STMFD R13!,{R2-R5,R9,R10,R14} ;Stack some registers
441 MOV R9,R12 ;Get my workspace
442
443 ; --- Go through the handlers list ---
444
445 LDR R2,idle__iHandlers ;Get my idle handlers list
446 10 TEQ R2,#0 ;Are we at the end
447 BEQ %20idle__dispatch ;Yes -- jump ahead
448 LDMIA R2,{R2,R3,R4,R10,R12} ;Load parameters to pass
449 MOV R14,PC ;Set return address
450 MOV PC,R4 ;Branch to handler
451 B %10idle__dispatch ;Try next handler
452 20 LDMFD R13!,{R2-R5,R9,R10,PC}^ ;Load registers if claimed
453
454 LTORG
455
456 ; --- idle__dispatchAlarm ---
457 ;
458 ; On entry: R0 == reason code returned from Wimp_Poll
459 ; R1 == pointer to block
460 ; R12 == pointer to workspace
461 ;
462 ; On exit: R0-R1 preserved
463
464 idle__dispatchAlarm ROUT
465
466 CMP R0,#0 ;Is it a NULL event
467 MOVNES PC,R14 ;No - return
468 STMFD R13!,{R0-R5,R9,R10,R14} ;Stack some registers
469 MOV R9,R12 ;Get my workspace
470
471 ; --- Go through the handlers list ---
472
473 LDR R2,idle__aHandlers ;Get my alarm handlers list
474 10 SWI OS_ReadMonotonicTime ;Get the current time
475 TEQ R2,#0 ;Are we at the end
476 BEQ %20idle__dispatchAlarm ;Yes -- jump ahead
477 LDMIB R2,{R3,R4,R10,R12} ;Load parameters to pass
478 SUBS R3,R0,R3 ;Call this handler?
479 BMI %20idle__dispatchAlarm ;No -- return
480 MOV R14,PC ;Set return address
481 MOV PC,R4 ;Branch to handler
482
483 ; --- Remove the handler just called ---
484
485 LDMIA R2,{R4} ;Get next (changed?) alarm
486 MOV R0,R2 ;Get the block
487 MOV R1,#list__size ;The size of the block
488 BL sub_free ;Free the block
489 STR R4,idle__aHandlers ;Store next in list head
490
491 ; --- And keep going ---
492
493 MOV R2,R4 ;Get next in list
494 B %10idle__dispatchAlarm ;Try next handler
495
496 ; --- Return to user ---
497
498 20 LDMFD R13!,{R0-R5,R9,R10,PC}^ ;Load registers if claimed
499
500 LTORG
501
502 ; --- idle_init ---
503 ;
504 ; On entry: --
505 ;
506 ; On exit: --
507 ;
508 ; Use: Initialises the idle system.
509
510 EXPORT idle_init
511 idle_init ROUT
512
513 STMFD R13!,{R0,R1,R9,R14} ;Stack some registers
514 WSPACE idle__wSpace,R9 ;Get my workspace
515
516 ; --- Are we already initialised? ---
517
518 LDR R0,idle__flags ;Get my flags
519 TST R0,#idle__INITED ;Are we initialised?
520 LDMNEFD R13!,{R0,R1,R9,PC}^ ;Yes -- return
521
522 ORR R0,R0,#idle__INITED ;Set initialised flag
523 STR R0,idle__flags ;And store them back
524
525 ; --- Clear rest of workspace ---
526
527 MOV R0,#0 ;Zero some registers
528 STR R0,idle__iHandlers ;Clear idle handler list
529 STR R0,idle__aHandlers ;Clear alarm handler list
530 MOV R0,#-1 ;So minimum time yet
531 STR R0,idle__minTime ;Set the word
532
533 ; --- Initialise event system ---
534
535 BL event_init ;Initialise event system
536
537 ; --- Set up a post filter for the idle system ---
538
539 ADR R0,idle__dispatch ;Call this routine
540 MOV R1,R9 ;Put my workspace in R12
541 BL event_postFilter ;Add it to post-filter list
542
543 ; --- Set up a post filter for the alarm system ---
544
545 ADR R0,idle__dispatchAlarm ;Call this routine
546 MOV R1,R9 ;Put my workspace in R12
547 BL event_postFilter ;Add it to post-filter list
548
549 ; --- Set up the pre filter ---
550
551 ADR R0,idle__preFilter ;Call this routine
552 MOV R1,R9 ;Put my workspace in R12
553 BL event_preFilter ;Add it to pre-filter list
554
555 ; --- That's it now ---
556
557 LDMFD R13!,{R0,R1,R9,PC}^ ;Return
558
559 LTORG
560
561 idle__wSpace DCD 0 ;My workspace pointer
562
563 ;----- Workspace ------------------------------------------------------------
564
565 ^ 0,R9
566 idle__wStart # 0
567
568 idle__flags # 4 ;Flags
569
570 idle__INITED EQU (1<<0) ;I've been initialised
571
572 idle__iHandlers # 4 ;Idle handler list
573 idle__aHandlers # 4 ;Alarm handler list
574 idle__minTime # 4 ;Minimum time to return with
575
576 idle__wSize EQU {VAR}-idle__wStart
577
578 ; --- list structure ---
579
580 ^ 0
581 list__next # 4 ;The next block
582 list__time # 4 ;Handler code
583 list__proc # 4 ;The time word
584 list__r10 # 4 ;R10 to call with
585 list__r12 # 4 ;R12 to call with
586
587 list__size # 0
588
589 AREA |Sapphire$$LibData|,CODE,READONLY
590
591 DCD idle__wSize
592 DCD idle__wSpace
593 DCD 0
594 DCD idle_init
595
596 ;----- That's all, folks ----------------------------------------------------
597
598 END