4 ; Idle event and alarm handling (TMA)
6 ; © 1994-1998 Straylight
9 ;----- Licensing note -------------------------------------------------------
11 ; This file is part of Straylight's Sapphire library.
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)
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.
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.
27 ;----- Standard header ------------------------------------------------------
32 ;----- External dependencies ------------------------------------------------
38 ;----- Main code ------------------------------------------------------------
40 AREA |Sapphire$$Code|,CODE,READONLY
42 ; --- idle__addToList ---
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
50 ; On exit: R0-R4 preserved
52 ; Use: Adds a rountine to the given list. Later added
53 ; routines are called first
57 STMFD R13!,{R0-R4,R14} ;Stack some registers
59 ; --- Allocate a block ---
61 MOV R0,#list__size ;Size to allocate
62 BL sub_alloc ;Allocate the block
63 BVS %01 ;Branch ahead if error
65 ; --- Fill the block in ---
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
71 LDMIA R13,{R1-R4} ;Get parameters
72 STMIB R0,{R1-R4} ;Store them in the block
74 ; --- And return to user ---
76 LDMFD R13!,{R0-R4,R14} ;Load back link
77 BICS PC,R14,#V_flag ;Return without error
79 ; --- Barf if an error occured ---
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
87 ; --- idle__removeFromList ---
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
95 ; On exit: All registers/flags preserved
97 ; Use: Removes a routine from the given list. All values are
103 STMFD R13!,{R0-R10,R12,R14}
105 ; --- Find the block ---
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
122 ; --- So now the block has been found ---
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 ^
131 ; --- And return to the user ---
133 LDMFD R13!,{R0-R10,R12,PC}^
137 ; --- idle_handler ---
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
144 ; On exit: May return an error
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
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
159 ; --- Check through the list ---
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?
169 BEQ %90idle_handler ;Yes -- return now then
170 B %10idle_handler ;Try next handler
172 ; --- Be careful not to alter flags ---
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
178 ; --- Cache the minimum return time ---
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
187 ; --- Return to client ---
189 90idle_handler LDMFD R13!,{R4-R9,PC}^ ;Return cunningly
191 ; --- idle_removeHandler ---
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
200 ; Use: Removes a routine from the idle handler list.
202 EXPORT idle_removeHandler
206 STMFD R13!,{R4-R6,R9,R14} ;Stack some registers
208 ; --- Free the handler ---
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
214 ; --- Find the new minimum frequency ---
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
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
230 ; --- idle_setAlarm ---
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
237 ; On exit: May return an error
239 ; Use: Adds a alarm to be called. The idle handing routine
240 ; may corrupt R10 and R12.
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
249 ; --- Allocate a block ---
251 MOV R0,#list__size ;Size to allocate
252 BL sub_alloc ;Allocate the block
253 BVS %02idle_setAlarm ;Branch ahead if error
255 ; --- Work out where to put it ---
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
268 ; --- Put the alarm in between R2 and R4 ---
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
275 ; --- Fill the block in ---
277 LDMIA R13,{R1-R4} ;Get parameters
278 STMIB R0,{R1-R4} ;Store them in the block
280 ; --- And return to user ---
282 LDMFD R13!,{R0-R4,R9,R14} ;Load back link
283 BICS PC,R14,#V_flag ;Return without error
285 ; --- Barf if an error occured ---
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
294 ; --- idle_removeAlarm ---
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
303 ; Use: Removes a routine from the idle handler list. It has
304 ; no effect if it doesn't exist.
306 EXPORT idle_removeAlarm
310 STMFD R13!,{R4-R6,R9,R14} ;Stack some registers
312 ; --- Free the handler ---
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
320 LDMFD R13!,{R4-R6,R9,R14} ;Load registers
321 BICS PC,R14,#V_flag ;Return without error
323 ; --- idle_removeAllAlarms ---
325 ; On entry: R0 == R10 value to look for
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
334 EXPORT idle_removeAllAlarms
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
342 ; --- Find the first block with the handle block ---
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
355 ; --- So now the block has been found ---
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
368 ; --- idle__preFilter ---
370 ; On entry: R0 == reason code return from Wimp_Poll
371 ; R1 == pointer to block
372 ; R2 == earliest time to return with NULL
374 ; On exit: R2 altered as appropriate
378 STMFD R13!,{R0,R5-R7,R9,R14}
381 ; --- Is the user requesting idles? ---
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
387 ; --- Get the minimum time to return with ---
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
395 ; --- Get the next time the quickest idle will call ---
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
402 ; --- And the time of the next alarm ---
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
413 ; --- Now set up the mask word and return time ---
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
422 ; --- The user is expecting idles, so please him ---
424 01 CMP R5,R2 ;Which is bigger?
425 MOVLT R2,R5 ;Swap if needed
426 LDMFD R13!,{R5-R7,R9,PC}^ ;...and return
428 ; --- idle__dispatch ---
430 ; On entry: R0 == reason code returned from Wimp_Poll
431 ; R1 == pointer to block
432 ; R12 == pointer to workspace
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
443 ; --- Go through the handlers list ---
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
456 ; --- idle__dispatchAlarm ---
458 ; On entry: R0 == reason code returned from Wimp_Poll
459 ; R1 == pointer to block
460 ; R12 == pointer to workspace
462 ; On exit: R0-R1 preserved
464 idle__dispatchAlarm ROUT
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
471 ; --- Go through the handlers list ---
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
483 ; --- Remove the handler just called ---
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
491 ; --- And keep going ---
493 MOV R2,R4 ;Get next in list
494 B %10idle__dispatchAlarm ;Try next handler
496 ; --- Return to user ---
498 20 LDMFD R13!,{R0-R5,R9,R10,PC}^ ;Load registers if claimed
508 ; Use: Initialises the idle system.
513 STMFD R13!,{R0,R1,R9,R14} ;Stack some registers
514 WSPACE idle__wSpace,R9 ;Get my workspace
516 ; --- Are we already initialised? ---
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
522 ORR R0,R0,#idle__INITED ;Set initialised flag
523 STR R0,idle__flags ;And store them back
525 ; --- Clear rest of workspace ---
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
533 ; --- Initialise event system ---
535 BL event_init ;Initialise event system
537 ; --- Set up a post filter for the idle system ---
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
543 ; --- Set up a post filter for the alarm system ---
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
549 ; --- Set up the pre filter ---
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
555 ; --- That's it now ---
557 LDMFD R13!,{R0,R1,R9,PC}^ ;Return
561 idle__wSpace DCD 0 ;My workspace pointer
563 ;----- Workspace ------------------------------------------------------------
568 idle__flags # 4 ;Flags
570 idle__INITED EQU (1<<0) ;I've been initialised
572 idle__iHandlers # 4 ;Idle handler list
573 idle__aHandlers # 4 ;Alarm handler list
574 idle__minTime # 4 ;Minimum time to return with
576 idle__wSize EQU {VAR}-idle__wStart
578 ; --- list structure ---
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
589 AREA |Sapphire$$LibData|,CODE,READONLY
596 ;----- That's all, folks ----------------------------------------------------