; ; idle.s ; ; Idle event and alarm handling (TMA) ; ; © 1994-1998 Straylight ; ;----- Licensing note ------------------------------------------------------- ; ; This file is part of Straylight's Sapphire library. ; ; Sapphire is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2, or (at your option) ; any later version. ; ; Sapphire is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with Sapphire. If not, write to the Free Software Foundation, ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ;----- Standard header ------------------------------------------------------ GET libs:header GET libs:swis ;----- External dependencies ------------------------------------------------ GET sapphire:sapphire GET sapphire:suballoc GET sapphire:event ;----- Main code ------------------------------------------------------------ AREA |Sapphire$$Code|,CODE,READONLY ; --- idle__addToList --- ; ; On entry: R0 == Time word ; R1 == pointer to routine to call ; R2 == R10 value to call routine ; R3 == R12 value to call routine with ; R4 == pointer to list head to use ; ; On exit: R0-R4 preserved ; ; Use: Adds a rountine to the given list. Later added ; routines are called first idle__addToList ROUT STMFD R13!,{R0-R4,R14} ;Stack some registers ; --- Allocate a block --- MOV R0,#list__size ;Size to allocate BL sub_alloc ;Allocate the block BVS %01 ;Branch ahead if error ; --- Fill the block in --- LDR R1,[R4] ;Get the list head STR R1,[R0,#list__next] ;Store in next field STR R0,[R4] ;Store new block at head LDMIA R13,{R1-R4} ;Get parameters STMIB R0,{R1-R4} ;Store them in the block ; --- And return to user --- LDMFD R13!,{R0-R4,R14} ;Load back link BICS PC,R14,#V_flag ;Return without error ; --- Barf if an error occured --- 01 ADD R13,R13,#4 ;Skip over R0,R1,R2 LDMFD R13!,{R1-R4,R14} ;Branch if error ORRS PC,R14,#V_flag ;Return with error LTORG ; --- idle__removeFromList --- ; ; On entry: R0 == Time word ; R1 == pointer to routine to be called ; R2 == R10 value routine is called with ; R3 == R12 value routine is called with ; R4 == pointer to list head to use ; ; On exit: All registers/flags preserved ; ; Use: Removes a routine from the given list. All values are ; compared. idle__removeFromList ROUT STMFD R13!,{R0-R10,R12,R14} ; --- Find the block --- MOV R5,#0 ;The previous pointer MOV R12,R4 ;Remember where the head is LDR R4,[R4] ;Get the head of the list 01 TEQ R4,#0 ;Are we at the end? LDMEQFD R13!,{R0-R10,R12,PC}^ ;Yes -- return LDR R10,[R4],#4 ;Get the next pointer LDMIA R4,{R6-R9} ;Load data from the block CMP R6,R0 ;Are routines the same? CMPEQ R7,R1 ;Yes -- and window/R4 handle? CMPEQ R8,R2 ;Yes -- R10 value? CMPEQ R9,R3 ;Yes -- R12 value? SUBNE R5,R4,#4 ;If no, remember previous MOVNE R4,R10 ;...get list pointer BNE %01 ;...and keep looking ; --- So now the block has been found --- SUB R0,R4,#4 ;Put the block in R0 MOV R1,#list__size ;Get the size BL sub_free ;Free the block CMP R5,#0 ;Was there a previous block STREQ R10,[R12,#0] ;No -- store next blk in head STRNE R10,[R5,#0] ;Yes -- store in prev next ^ ; --- And return to the user --- LDMFD R13!,{R0-R10,R12,PC}^ LTORG ; --- idle_handler --- ; ; On entry: R0 == how frequently to call ; R1 == pointer to routine to call ; R2 == R10 value to call routine with ; R3 == R12 value to call routine with ; ; On exit: May return an error ; ; Use: Adds a routine to the idle handler list. Later added ; routines are called first. The idle handing routine ; may corrupt R10 and R12. ; A handler is only addeded if an identical one does not ; already exist. EXPORT idle_handler idle_handler ROUT BIC R14,R14,#V_flag ;Assume it's all OK STMFD R13!,{R4-R9,R14} ;Save some registers WSPACE idle__wSpace,R9 ;Get my workspace pointer ; --- Check through the list --- LDR R4,idle__iHandlers ;Get my idle handlers list 10idle_handler TEQ R4,#0 ;Are we at the end BEQ %20idle_handler ;Yes -- jump ahead LDMIA R4,{R4-R8} ;Load parameters to pass CMP R5,R0 ;Is this identical? CMPEQ R6,R1 CMPEQ R7,R2 CMPEQ R8,R3 BEQ %90idle_handler ;Yes -- return now then B %10idle_handler ;Try next handler ; --- Be careful not to alter flags --- 20idle_handler ADR R4,idle__iHandlers ;Get the idle handlers BL idle__addToList ;Add the routine to the list LDMVSFD R13!,{R4-R9,PC} ;Return if it failed ; --- Cache the minimum return time --- LDR R4,idle__minTime ;Get the current minimum time CMP R4,#-1 ;Is there one? STREQ R0,idle__minTime ;No -- store the new one LDMEQFD R13!,{R4-R9,PC}^ ;...and return CMP R0,R4 ;Is the new time lower STRLE R0,idle__minTime ;Yes -- Store the new one ; --- Return to client --- 90idle_handler LDMFD R13!,{R4-R9,PC}^ ;Return cunningly ; --- idle_removeHandler --- ; ; On entry: R0 == How frequently it was called ; R1 == pointer to routine called ; R2 == R10 value routine is called with ; R3 == R12 value routine is called with ; ; On exit: -- ; ; Use: Removes a routine from the idle handler list. EXPORT idle_removeHandler idle_removeHandler ROUT STMFD R13!,{R4-R6,R9,R14} ;Stack some registers ; --- Free the handler --- WSPACE idle__wSpace,R9 ;Get my workspace pointer ADR R4,idle__iHandlers ;Get the idle handlers BL idle__removeFromList ;Remove routine from the list ; --- Find the new minimum frequency --- MOV R5,#-1 ;Lowest time found LDR R4,[R4] ;Get the first handler 00 TEQ R4,#0 ;Is it the end? BEQ %01 ;Yes -- finish LDR R6,[R4,#list__time] ;Load the frequency CMP R6,R5 ;Is it lower than lowest MOVLO R5,R6 ;Yes -- remember it LDR R4,[R4] ;Get next in list B %00 ;Try the rest 01 STR R5,idle__minTime ;Store it found value LDMFD R13!,{R4-R6,R9,R14} ;Load registers BICS PC,R14,#V_flag ;Return without error ; --- idle_setAlarm --- ; ; On entry: R3 == Time to call ; R1 == pointer to routine to call ; R2 == R10 value to call routine with ; R3 == R12 value to call routine with ; ; On exit: May return an error ; ; Use: Adds a alarm to be called. The idle handing routine ; may corrupt R10 and R12. EXPORT idle_setAlarm idle_setAlarm ROUT STMFD R13!,{R0-R4,R9,R14} ;Stack some registers WSPACE idle__wSpace,R9 ;Get my workspace MOV R1,R0 ;Put requested time in R1 ; --- Allocate a block --- MOV R0,#list__size ;Size to allocate BL sub_alloc ;Allocate the block BVS %02idle_setAlarm ;Branch ahead if error ; --- Work out where to put it --- MOV R2,#0 ;Previous alarm LDR R4,idle__aHandlers ;Get the list head 00idle_setAlarm CMP R4,#0 ;Are we at the end BEQ %01idle_setAlarm ;Put it in here LDR R3,[R4,#4] ;Get the time due CMP R3,R1 ;Compare with new alarm BGT %01idle_setAlarm ;If bigger, put it here MOV R2,R4 ;Remember this alarm LDR R4,[R4,#0] ;Get next alarm in list B %00idle_setAlarm ; --- Put the alarm in between R2 and R4 --- 01idle_setAlarm STR R4,[R0,#list__next] ;Store in next field CMP R2,#0 ;Was there a previous alarm STREQ R0,idle__aHandlers ;No, store new block at head STRNE R0,[R2,#0] ;Yes, store in its next field ; --- Fill the block in --- LDMIA R13,{R1-R4} ;Get parameters STMIB R0,{R1-R4} ;Store them in the block ; --- And return to user --- LDMFD R13!,{R0-R4,R9,R14} ;Load back link BICS PC,R14,#V_flag ;Return without error ; --- Barf if an error occured --- 02idle_setAlarm ADD R13,R13,#4 ;Skip over R0 LDMFD R13!,{R1-R4,R9,R14} ;Branch if error ORRS PC,R14,#V_flag ;Return with error LTORG ; --- idle_removeAlarm --- ; ; On entry: R0 == When it was to be called ; R1 == pointer to routine called ; R2 == R10 value routine is called with ; R3 == R12 value routine is called with ; ; On exit: -- ; ; Use: Removes a routine from the idle handler list. It has ; no effect if it doesn't exist. EXPORT idle_removeAlarm idle_removeAlarm ROUT STMFD R13!,{R4-R6,R9,R14} ;Stack some registers ; --- Free the handler --- WSPACE idle__wSpace,R9 ;Get my workspace pointer ADR R4,idle__aHandlers ;Get the idle handlers BL idle__removeFromList ;Remove routine from the list ; --- And return --- LDMFD R13!,{R4-R6,R9,R14} ;Load registers BICS PC,R14,#V_flag ;Return without error ; --- idle_removeAllAlarms --- ; ; On entry: R0 == R10 value to look for ; ; On exit: -- ; ; Use: Removes all alarms with the handle that was passed to them ; to be put into R10. You should not remove an alarm within ; an alarm handler. EXPORT idle_removeAllAlarms idle_removeAllAlarms ROUT STMFD R13!,{R0-R10,R14} ;Stack some registers WSPACE idle__wSpace,R9 ;Get the alarm handler list MOV R10,R0 ;Remember handle ; --- Find the first block with the handle block --- MOV R2,#0 ;The previous pointer LDR R3,idle__aHandlers ;Get the head of the list 00 TEQ R3,#0 ;Are we at the end? LDMEQFD R13!,{R0-R10,PC}^ ;Yes -- return LDR R4,[R3],#4 ;Get the next pointer LDMIA R3,{R5-R8} ;Load data from the block CMP R7,R10 ;Are handles the same? SUBNE R2,R3,#4 ;If no, remember previous MOVNE R3,R4 ;...point to the next block BNE %00 ;...and keep looking ; --- So now the block has been found --- SUB R0,R3,#4 ;Put the block in R0 MOV R1,#list__size ;Get the size BL sub_free ;Free the block CMP R2,#0 ;Was there a previous block STREQ R4,idle__aHandlers ;No -- store next blk in head STRNE R4,[R2,#0] ;Yes -- store in prev next ^ MOV R3,R4 ;Point at the next block B %00 ;Find more alarms LTORG ; --- idle__preFilter --- ; ; On entry: R0 == reason code return from Wimp_Poll ; R1 == pointer to block ; R2 == earliest time to return with NULL ; ; On exit: R2 altered as appropriate idle__preFilter ROUT STMFD R13!,{R0,R5-R7,R9,R14} MOV R9,R12 ; --- Is the user requesting idles? --- TST R0,#1 ;Is it masked off CMPEQ R2,#0 ;If no, just Wimp_Poll? LDMEQFD R13!,{R0,R5-R7,R9,PC}^ ;Return PDQ ; --- Get the minimum time to return with --- LDR R5,idle__iHandlers ;Get the idle handlers list LDR R6,idle__aHandlers ;Get the alarm handlers list CMP R5,#0 ;Do we have idle handlers? CMPEQ R6,#0 ;If not, any alarm handlers? LDMEQFD R13!,{R0,R5-R7,R9,PC}^ ;No idles/alarms -- return ; --- Get the next time the quickest idle will call --- LDR R7,idle__minTime ;Get the minimum frequency CMP R7,#-1 ;Is there one? SWINE OS_ReadMonotonicTime ;Yes -- get the current time ADDNE R5,R7,R0 ;...and time to return with ; --- And the time of the next alarm --- CMP R6,#0 ;Are there any alarms? BEQ %00 ;No -- R5 contains quickest LDR R6,[R6,#list__time] ;Get time of the next alarm CMP R7,#-1 ;Was there an idle handler? MOVEQ R5,R6 ;No, R6 is lowest BEQ %00 ;...branch ahead CMP R6,R5 ;Is it before next idle MOVLT R5,R6 ;Yes, use this time ; --- Now set up the mask word and return time --- 00 LDR R0,[R13],#4 ;Get event mask back TST R0,#1 ;Is the user expecting idles BEQ %01 ;Yes -- Do a comparison BIC R0,R0,#1 ;Clear the mask bit MOV R2,R5 ;Put the minimum time in R2 LDMFD R13!,{R5-R7,R9,PC}^ ;...and return ; --- The user is expecting idles, so please him --- 01 CMP R5,R2 ;Which is bigger? MOVLT R2,R5 ;Swap if needed LDMFD R13!,{R5-R7,R9,PC}^ ;...and return ; --- idle__dispatch --- ; ; On entry: R0 == reason code returned from Wimp_Poll ; R1 == pointer to block ; R12 == pointer to workspace ; ; On exit: -- idle__dispatch ROUT CMP R0,#0 ;Is it a NULL event MOVNES PC,R14 ;No - return STMFD R13!,{R2-R5,R9,R10,R14} ;Stack some registers MOV R9,R12 ;Get my workspace ; --- Go through the handlers list --- LDR R2,idle__iHandlers ;Get my idle handlers list 10 TEQ R2,#0 ;Are we at the end BEQ %20idle__dispatch ;Yes -- jump ahead LDMIA R2,{R2,R3,R4,R10,R12} ;Load parameters to pass MOV R14,PC ;Set return address MOV PC,R4 ;Branch to handler B %10idle__dispatch ;Try next handler 20 LDMFD R13!,{R2-R5,R9,R10,PC}^ ;Load registers if claimed LTORG ; --- idle__dispatchAlarm --- ; ; On entry: R0 == reason code returned from Wimp_Poll ; R1 == pointer to block ; R12 == pointer to workspace ; ; On exit: R0-R1 preserved idle__dispatchAlarm ROUT CMP R0,#0 ;Is it a NULL event MOVNES PC,R14 ;No - return STMFD R13!,{R0-R5,R9,R10,R14} ;Stack some registers MOV R9,R12 ;Get my workspace ; --- Go through the handlers list --- LDR R2,idle__aHandlers ;Get my alarm handlers list 10 SWI OS_ReadMonotonicTime ;Get the current time TEQ R2,#0 ;Are we at the end BEQ %20idle__dispatchAlarm ;Yes -- jump ahead LDMIB R2,{R3,R4,R10,R12} ;Load parameters to pass SUBS R3,R0,R3 ;Call this handler? BMI %20idle__dispatchAlarm ;No -- return MOV R14,PC ;Set return address MOV PC,R4 ;Branch to handler ; --- Remove the handler just called --- LDMIA R2,{R4} ;Get next (changed?) alarm MOV R0,R2 ;Get the block MOV R1,#list__size ;The size of the block BL sub_free ;Free the block STR R4,idle__aHandlers ;Store next in list head ; --- And keep going --- MOV R2,R4 ;Get next in list B %10idle__dispatchAlarm ;Try next handler ; --- Return to user --- 20 LDMFD R13!,{R0-R5,R9,R10,PC}^ ;Load registers if claimed LTORG ; --- idle_init --- ; ; On entry: -- ; ; On exit: -- ; ; Use: Initialises the idle system. EXPORT idle_init idle_init ROUT STMFD R13!,{R0,R1,R9,R14} ;Stack some registers WSPACE idle__wSpace,R9 ;Get my workspace ; --- Are we already initialised? --- LDR R0,idle__flags ;Get my flags TST R0,#idle__INITED ;Are we initialised? LDMNEFD R13!,{R0,R1,R9,PC}^ ;Yes -- return ORR R0,R0,#idle__INITED ;Set initialised flag STR R0,idle__flags ;And store them back ; --- Clear rest of workspace --- MOV R0,#0 ;Zero some registers STR R0,idle__iHandlers ;Clear idle handler list STR R0,idle__aHandlers ;Clear alarm handler list MOV R0,#-1 ;So minimum time yet STR R0,idle__minTime ;Set the word ; --- Initialise event system --- BL event_init ;Initialise event system ; --- Set up a post filter for the idle system --- ADR R0,idle__dispatch ;Call this routine MOV R1,R9 ;Put my workspace in R12 BL event_postFilter ;Add it to post-filter list ; --- Set up a post filter for the alarm system --- ADR R0,idle__dispatchAlarm ;Call this routine MOV R1,R9 ;Put my workspace in R12 BL event_postFilter ;Add it to post-filter list ; --- Set up the pre filter --- ADR R0,idle__preFilter ;Call this routine MOV R1,R9 ;Put my workspace in R12 BL event_preFilter ;Add it to pre-filter list ; --- That's it now --- LDMFD R13!,{R0,R1,R9,PC}^ ;Return LTORG idle__wSpace DCD 0 ;My workspace pointer ;----- Workspace ------------------------------------------------------------ ^ 0,R9 idle__wStart # 0 idle__flags # 4 ;Flags idle__INITED EQU (1<<0) ;I've been initialised idle__iHandlers # 4 ;Idle handler list idle__aHandlers # 4 ;Alarm handler list idle__minTime # 4 ;Minimum time to return with idle__wSize EQU {VAR}-idle__wStart ; --- list structure --- ^ 0 list__next # 4 ;The next block list__time # 4 ;Handler code list__proc # 4 ;The time word list__r10 # 4 ;R10 to call with list__r12 # 4 ;R12 to call with list__size # 0 AREA |Sapphire$$LibData|,CODE,READONLY DCD idle__wSize DCD idle__wSpace DCD 0 DCD idle_init ;----- That's all, folks ---------------------------------------------------- END