chiark / gitweb /
JPEG support and other fixes from Nick Clark
[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