; Nice module, written by Vincent Lefvre
; Freeware - use it at your own risk.

; $Id: nice 2.1 2003/05/25 10:05:24 vlefevre Exp $

; Assemble with !ObjAsm, and link with !Tracker lib if Debug is TRUE.

		GET	h:RegNames
		GET	h:SWINames
		GET	h:RCSsupport

; If you don't have the Tracker library, comment out the
; following GET line and uncomment the two macros.

		GET	h:DebugHdr

;		MACRO
;$label		DBSET	$flags, $cc
;$label
;		MEND
;
;		MACRO
;$label		DBF	$argstr, $cc
;$label
;		MEND

		GBLL	Debug
Debug		SETL	{FALSE}

		GETVERS	"$Revision: 2.1 $"
		GETDATE	"$Date: 2003/05/25 10:05:24 $"

		GBLS	NAME
NAME		SETS	"Nice"
		GBLS	AUTHOR
AUTHOR		SETS	"Vincent Lefvre"
		GBLS	VERSION
VERSION		SETS	VERS:CC:" (":CC:DATE:CC:")"

MAXT		EQU	20	;Maximum number of niced tasks.
MAXTNL		EQU	32	;Maximum length of a task name (display).

Serv_WCD	EQU	&53	;Service: Wimp_Closedown.
Serv_FMD	EQU	&88	;Service: Filter Manager Dying.

UsrMsg		EQU	17
UsrMsgRec	EQU	18

TS		EQU	16

NPLUS		EQU	&40000000

		^	0
		#	4	;Task handle.
LIST		#	4	;Pointer to the nice+ list.
NT		#	1	;Number of niced tasks.
		#	3	;Not used.
TASKS		#	TS*MAXT	;Task handles and other data.
BUFFER		#	256	;Buffer for Wimp_Poll and commands.
		[	Debug
STACK		#	256	;Stack.
		]
SIZE		#	0

		^	BUFFER	;Buffers for *nice...
ETB		#	16
TNB		#	240

		AREA	Nice, CODE, PIC, READONLY
		ENTRY

BaseAddr	DCD	RM_Start  -BaseAddr
		DCD	RM_Init   -BaseAddr
		DCD	RM_Die    -BaseAddr
		DCD	RM_Service-BaseAddr
		DCD	RM_Title  -BaseAddr
		DCD	RM_Help   -BaseAddr
		DCD	RM_HCtable-BaseAddr
		DCD	0,0,0,0,0
		DCD	RM_Flags  -BaseAddr

RM_Flags	DCD	1		;32-bit compatible.

; *************************
; Code for command nicestat
; *************************

nicestat	DBF	"*nicestat\n"
		LDR	R12, [R12, #0]	;R12: pointer to workspace.
		MOV	R2, #16
		LDRB	R0, [R12, #NT]	;Number of niced tasks.
		ADD	R1, R12, #BUFFER
		CMP	R0, #MAXT
		BHI	err_bad_nt	;Return with error if too many nices.
		STMFD	SP!, {LR}
		MOV	R6, R0		;R6: number of niced tasks.
		SWI	XOS_ConvertCardinal1
		SWIVC	XOS_Write0	;Write "%d niced task(s).\n"...
		ADRVC	R0, nicedtasks
		SWIVC	XOS_Write0
		SWIVC	XOS_NewLine
		LDMVSFD	SP!, {PC}	;Return with V=1 if error.
		ADD	R4, R12, #TASKS-TS
		MOV	R5, #MAXT
nicestat_loop1	LDR	R0, [R4, #TS]!	;Task handle or 0.
		AND	R2, R0, #&FF	;R2: nice time.
		MOVS	R0, R0, LSR #16
		BEQ	nicestat_next	;Branch if 0 (empty entry).
		SUB	R6, R6, #1
		SWI	XTaskManager_TaskNameFromHandle
		LDMVSFD	SP!, {PC}	;Return with V=1 if error.
		MOV	R1, R0		;Pointer to task name.
		MOV	R3, #MAXTNL	;R3 will be MAXTNL - TaskNameLength.
wr_taskname	LDRB	R0, [R1], #1	;Next character of task name.
		CMP	R0, #" "	;End of task name? C = 0 --> yes.
		SWICS	XOS_WriteC
		LDMVSFD	SP!, {PC}	;Return with V=1 if error.
		SUBCS	R3, R3, #1
		BCS	wr_taskname
		MOV	R0, R2		;Nice time.
		ADD	R1, R12, #BUFFER
		MOV	R2, #4
		SWI	XOS_ConvertCardinal1
		LDMVSFD	SP!, {PC}	;Return with V=1 if error.
		ADDS	R3, R3, R2
		MOVMI	R3, #0
write_spaces	SWI	XOS_WriteI+" "	;Write R3+1 space characters.
		LDMVSFD	SP!, {PC}	;Return with V=1 if error.
		SUBS	R3, R3, #1
		BCS	write_spaces
		SWI	XOS_Write0	;Write the nice time.
		LDMVSFD	SP!, {PC}	;Count the number of 1's in
		MOV	R0, #0		;the activity vector...
		LDR	R1, [R4, #8]
count_loop1	MOVS	R1, R1, LSR #1
		ADC	R0, R0, #0
		BNE	count_loop1
		LDR	R1, [R4, #12]
count_loop2	MOVS	R1, R1, LSR #1
		ADC	R0, R0, #0
		BNE	count_loop2
		LDR	R2, spaces	;4 space characters.
		ADD	R1, R12, #BUFFER
		STR	R2, [R1], #4
		MOV	R2, #4
		SWI	XOS_ConvertCardinal1
		ADDVC	R0, R12, #BUFFER
		SWIVC	XOS_Write0
		SWIVC	XOS_NewLine
		LDMVSFD	SP!, {PC}	;Return with V=1 if error.
nicestat_next	SUBS	R5, R5, #1
		BNE	nicestat_loop1
		SWI	XOS_NewLine
		LDRVC	R5, [R12, #LIST]
		LDMVSFD	SP!, {PC}	;Return with V=1 if error.
		TEQ	R5, #0
		BEQ	empty_list
		SWI	XOS_WriteS
		DCB	"Task names in the nice+ list:",0
		ALIGN
		SWIVC	XOS_NewLine
nicestat_loop2	ADD	R0, R5, #5
		SWIVC	XOS_Write0
		SWIVC	XOS_NewLine
		LDMVSFD	SP!, {PC}	;Return with V=1 if error.
		LDR	R5, [R5, #0]
		TEQ	R5, #0
		BNE	nicestat_loop2
		B	nicestat_end
empty_list	SWI	XOS_WriteS
		DCB	"Empty nice+ list.",0
		ALIGN
		SWIVC	XOS_NewLine
		LDMVSFD	SP!, {PC}	;Return with V=1 if error.
nicestat_end	CMP	R6, #0		;V=0.
		LDMEQFD	SP!, {PC}
		LDMFD	SP!, {LR}	;Return with error if bad nr of NT...
err_bad_nt	MOV	R0, #&80000000
		ADDS	R0, R0, R0	;V=1.
		ADR	R0, bad_nt
		MOV	PC, LR

spaces		DCB	"    "
nicedtasks	DCB	" niced task(s).",0
		ALIGN

bad_nt		DCD	0
		DCB	"Bad number of niced tasks, the system may crash",0
		ALIGN

; **********************
; Code for command nice+
; **********************

niceplus	DBF	"*nice+\n"
		MOV	R6, LR
		BL	read_param	;Read the parameters.
		SUBLS	R2, R2, #1	;R2: task name length.
		DBF	"  task name length: %2#w\n"
		ORR	R4, R4, #NPLUS
		ADD	R5, R12, #LIST
		B	nplus_next
nplus_outer	ADD	R14, R14, #5	;R14: ptr to the task name in the cell.
nplus_inner	LDRB	R0, [R3], #1
		LDRB	R1, [R14], #1
		TEQ	R0, #0
		TEQEQ	R1, #0
		BEQ	nplus_found
		TEQ	R0, R1
		BEQ	nplus_inner
		LDR	R5, [R5, #0]	;Next cell...
nplus_next	LDR	R14, [R5]	;R14: ptr to the next cell or 0.
		ADD	R3, R12, #TNB	;R3: ptr to the task name passed in arg.
		TEQ	R14, #0
		BNE	nplus_outer

; The task name has not been found in the list. R2: task name length.
; R4: nice value + flag. R5: will contain the address of last cell.

		BICS	R0, R4, #NPLUS
		BEQ	nice_loop	;Branch if nice value = 0.
		ADD	R3, R2, #6	;Size of the cell to create.
		MOV	R0, #6
		SWI	XOS_Module	;Claim RMA space.
		MOVVS	PC, R6		;Return with V=1 if error.
		STR	R2, [R5]	;Address of the cell.
		MOV	R0, #0
		STR	R0, [R2], #4	;Store 0 (end of list).
		STRB	R4, [R2], #1	;Store the nice value.
		ADD	R1, R12, #TNB	;R1: ptr to the task name passed in arg.
		DBF	"  new cell for task '%1s'\n"
nplus_loop	LDRB	R0, [R1], #1	;Copy the task name.
		TEQ	R0, #0
		STRB	R0, [R2], #1
		BNE	nplus_loop
		B	nice_loop	;Branch to nice_loop with R0 = 0.

; The task name has been found in the list.
; R0 = 0. R4: nice value + flag. R5: address of ptr to the cell.

nplus_found	BICS	R1, R4, #NPLUS
		LDR	R2, [R5]	;R2: ptr to the cell.
		STRNEB	R4, [R2, #4]	;If nice value <> 0: renice.

; Nice value = 0. Remove cell R1...

		DBF	"  remove the cell\n", EQ
		LDREQ	R1, [R2]
		MOVEQ	R0, #7
		STREQ	R1, [R5]
		SWIEQ	XOS_Module	;Free RMA space.
		MOV	R0, #0
		B	nice_loop	;Branch to nice_loop.

; **************************
; Routine for nice and nice+
; **************************

; Read the parameters.
; In: R6 = return address of the command.
; Out: R4 = nice time. [WS+TNB]: zero-terminated task name.
;      HI --> R2 = task name length.
;      LS --> R2 = task name length + 1.

read_param	LDR	R12, [R12, #0]	;R12: pointer to workspace.
		MOV	R5, LR
		MOV	R1, R0		;Read the 1st parameter...
		MOV	R0, #10+(3<<30)	;Note: nice time must be in [0,255].
		SWI	XOS_ReadUnsigned
		MOVVS	PC, R6		;Return with V=1 if error.
		DBF	"  first parameter: %2#w\n"
		MOV	R4, R2		;R4: value (nice time).

		MOV	R0, R1		;Read the 2nd parameter...
		ADD	R1, R12, #TNB	;Address of the buffer.
		MOV	R2, #240	;Size of the buffer.
		SWI	XOS_GSTrans	;GSTrans the task name.
		MOVVS	PC, R6		;Return with V=1 if error.
		BCS	err_overflow	;Return with error if buffer overflow.
		MOV	R0, #0
		STRB	R0, [R1,R2]!	;Zero-terminated string.
		LDRB	R3, [R1, #-1]
		CMP	R3, #' '
		STRLSB	R0, [R1, #-1]	;Strip the possible ending ctrl char.
		MOV	PC, R5		;Return to the command.

err_overflow	MOV	R0, #&80000000
		ADDS	R0, R0, R0	;V=1.
		ADR	R0, overflow
		MOV	PC, LR

; *********************
; Code for command nice
; *********************

nice		DBF	"*nice\n"
		MOV	R6, LR
		BL	read_param	;Read the parameters.

; R0 = 0. R4.b = nice time. R4 & NPLUS <> 0 if branch from nice+.

nice_loop	ADD	R1, R12, #ETB	;Look for the task handle...
		MOV	R2, #16
		SWI	XTaskManager_EnumerateTasks
		MOVVS	PC, R6		;Return with V=1 if error.
		LDR	R2, [R12, #ETB+4]
		ADD	R1, R12, #TNB
comp_loop	LDRB	R5, [R2], #1	;Name comparison.
		LDRB	R3, [R1], #1
		CMP	R3, #0
		BEQ	task_found	;Branch if the name matches.
		TEQ	R3, R5
		BEQ	comp_loop
		TEQ	R0, #0		;The name doesn't match, next task...
		BPL	nice_loop
		DBF	"  task not found\n"
		EOR	R4, R4, #NPLUS
		ADDS	R4, R4, R4	;V = 1 iff branch from nice.
		ADRVS	R0, not_found
		MOV	PC, R6		;Return with V = 0 or 1.

; The task has been found. [R12, #ETB]: task handle. R4.b: nice time.
; V = 0.

task_found	LDR	R3, [R12, #ETB]	;R3: task handle.
		DBF	"  task handle: &%3h\n"
		ADD	R2, R12, #TASKS-TS
		MOV	R3, R3, LSL #16
		MOV	R3, R3, LSR #16	;Clear the high-order half-word.
		MOV	R1, #MAXT
task_loop1	LDR	R0, [R2, #TS]!	;Next task handle or 0.
		EORS	R0, R3, R0, LSR #16
		BEQ	renice		;Branch with V=0 if task already niced.
		SUBS	R1, R1, #1
		BNE	task_loop1
		ANDS	R4, R4, #&FF
		MOVEQ	PC, R6		;Return with no error if nice time = 0.

; The task will be added to the table of the niced tasks.

		LDRB	R5, [R12, #NT]	;R5: number of niced tasks.
		CMP	R5, #MAXT
		RSBEQS	R0, R5, #&80000000 ;V = 1 iff R5 = MAXT.
		ADRVS	R0, too_many
		MOVVS	PC, R6		;Return with error if too many nices.
		DBF	"add task &%3h\n"
		ADD	R2, R12, #TASKS-TS
task_loop2	LDR	R0, [R2, #TS]!	;Next task handle or 0.
		TEQ	R0, #0
		BNE	task_loop2
		ORR	R4, R4, R3, LSL #16
		STR	R4, [R2]	;Store the task handle and nice time.
		STR	R0, [R2, #8]	;Activity vector = 0
		STR	R0, [R2, #12]	;(two 32-bit words).
		BL	filter_data
		SWI	XFilter_RegisterPostFilter
		MOVVS	PC, R6		;Return with V=1 if error.
		SWI	XOS_ReadMonotonicTime
		STR	R0, [R2, #4]	;Store monotonic time to the table.
		ADDS	R5, R5, #1
		STRB	R5, [R12, #NT]	;Update the number of niced tasks.
		DBF	"--> %5#w niced task(s)\n"
		MOV	PC, R6		;Return with no error.

renice		ANDS	R4, R4, #&FF
		STRNEB	R4, [R2]	;New nice time, if <> 0.
		MOVNE	PC, R6
		STMFD	SP!, {R0-R4,R6}	;Otherwise, remove the task...
		B	remove

; *********
; Some data
; *********

task		DCB	"TASK"

RM_Help		DCB	NAME,9,9,VERSION," by ",AUTHOR,0

		ALIGN
RM_HCtable	DCB	"Desktop_"
RM_Title	DCB	NAME,0
		ALIGN
		DCD	Desktop-BaseAddr,0,0,Help_Desktop-BaseAddr
		DCB	"nice",0
		ALIGN
		DCD	nice-BaseAddr,&020102,Snice-BaseAddr,Snice-BaseAddr
		DCB	"nice+",0
		ALIGN
		DCD	niceplus-BaseAddr,&020102,Snice-BaseAddr,Snice-BaseAddr
		DCB	"nicestat",0
		ALIGN
		DCD	nicestat-BaseAddr,0,0,Hnicestat-BaseAddr
		DCB	0

Help_Desktop	DCB	"Do not use *",27,0,", use *Desktop instead.",0
Snice		DCB	27,14,"time> <task>",0
Hnicestat	DCB	"*",27,0,27,32,27,2,"nice statistics.",0

		ALIGN
messages	DCD	&400C2	;TaskInitialise.

; Note: zero word follows.

ServiceTable	DCD	0, RM_FastServ-BaseAddr, Serv_WCD, Serv_FMD

; Note: zero word follows.

; ***********************
; Error messages for nice
; ***********************

overflow	DCD	0
		DCB	"Buffer overflow",0
		ALIGN

too_many	DCD	0
		DCB	"Too many niced tasks",0
		ALIGN

not_found	DCD	0
		DCB	"Task not found",0
		ALIGN

; *******************
; Routine filter_data
; *******************

filter_data	ADR	R0, RM_Title	;Filter name: Nice.
		ADR	R1, filter	;Filter routine.
		MVN	R4, #1		;Event mask: null reason.
		MOV	PC, LR

; **************
; Filter routine
; **************

filter		TEQ	R0, #0
		MOVNE	PC, LR		;Return unless null reason.
		STMFD	SP!, {R1,LR}
		LDR	R1, [R12, #4]	;Old monotonic time.
		SWI	XOS_ReadMonotonicTime
		LDMVSFD	SP!, {R1,PC}
		STR	R0, [R12, #4]	;New monotonic time.
		SUB	R0, R0, R1	;Wimp_Poll time.
		LDRB	R1, [R12, #1]	;Previous Wimp_Poll time.
		CMP	R0, #&FF
		MOVHI	R0, #&FF
		STRB	R0, [R12, #1]
		ADD	R0, R0, R1	;R0: sum of the last 2 Wimp_Poll times.
		LDRB	R1, [R12]	;R1: nice time.
		CMP	R0, R1		;Last 2 Wimp_Poll times < nice time?
		MOVCC	R0, #0		;Yes, R0 = 0. No, R0 = -1.
		LDR	R1, [R12, #8]	;Update the activity vector...
		MVNCS	R0, #0
		MOVS	R1, R1, RRX
		STR	R1, [R12, #8]
		LDR	R1, [R12, #12]
		MOV	R1, R1, RRX
		STR	R1, [R12, #12]
		LDMFD	SP!, {R1,PC}

; *****************
; Desktop_Nice code
; *****************

Desktop		MOV	R6, LR
		MOV	R2, R0
		ADR	R1, RM_Title
		MOV	R0, #2
		SWI	XOS_Module	;Enter Nice.
		MOV	PC, R6

; **********
; Start code
; **********

RM_Start	LDR	R6, [R12]	;R6: pointer to workspace.
		[	Debug
		ADD	SP, R6, #SIZE
		DBF	"RM_Start\n"
		]

		LDR	R0, [R6]	;Task handle or 0.
		MOV	R5, #0
		TEQ	R0, #0
		LDR	R1, task	;"TASK".
		BEQ	start2
		SWI	Wimp_CloseDown
		STR	R5, [R6]	;Reset task handle.

start2		MOV	R0, #300	;Last Wimp version known: 3.00.
		ADR	R2, RM_Title
		ADR	R3, messages
		SWI	XWimp_Initialise
		LDRVS	R1, task
		MOVVS	R0, #200	;If init failed, try ROS 2.00.
		SWIVS	XWimp_Initialise
		MOVVS	R3, R2
		SWIVS	OS_ExitAndDie
		STR	R1, [R6]	;Store task handle.

; Wimp_Poll loop.

loop0		ADR	R3, RM_Title
loop		MOV	R0, #&31	;Mask: &3831.
		ORR	R0, R0, #&3800
		ADD	R1, R6, #BUFFER	;256-byte block.
		SWI	XWimp_Poll
		SWIVS	OS_ExitAndDie
		DBF	"Event %0#w\n"
		TEQ	R0, #UsrMsg	;UserMessage?
		TEQNE	R0, #UsrMsgRec	;UserMessage_Recorded?
		BNE	loop		;No, loop.
		LDR	R0, [R1, #16]	;Message code.
		TEQ	R0, #0		;Quit?
		SWIEQ	OS_ExitAndDie	;Yes, exit and die...
		SUB	R0, R0, #&40000
		TEQ	R0, #&C2	;TaskInitialise?
		BNE	loop		;No, loop.

; Message TaskInitialise.

		DBF	"Message TaskInitialise\n"
		LDR	R7, [R6, #LIST]
		TEQ	R7, #0
		BEQ	loop		;... if the nice+ list is empty.
		LDR	R3, [R6, #BUFFER+4] ;Task handle of the starting app.
		[	Debug		;Display task name in Tracker...
		DBF	"Task '"
		ADD	R9, R6, #BUFFER+28
taskdbg_loop	LDRB	R11, [R9], #1
		CMP	R11, #' '
		DBF	"%Bc", CS
		BCS	taskdbg_loop
		DBF	"'\n"
		]
taskinit_loop1	ADD	R8, R7, #5	;R8: next task name of the nice+ list.
		ADD	R9, R6, #BUFFER+28 ;R9: name of the starting task.
taskinit_cmp	LDRB	R10, [R8], #1	;Compare the names...
		LDRB	R11, [R9], #1
		TEQ	R10, #0
		BEQ	taskinit_z
		TEQ	R10, R11
		BEQ	taskinit_cmp
taskinit_next	LDR	R7, [R7, #0]	;Names are different. Next cell...
		TEQ	R7, #0
		BNE	taskinit_loop1	;Loop unless no more cells.
		B	loop0

taskinit_z	CMP	R11, #' '
		BCS	taskinit_next
		LDRB	R5, [R6, #NT]	;R5: number of niced tasks.
		CMP	R5, #MAXT
		BEQ	loop0		;... if too many nices.
		DBF	"add task &%3h\n"
		LDRB	R4, [R7, #4]	;R4: nice time.
		ADD	R2, R6, #TASKS-TS
taskinit_loop2	LDR	R0, [R2, #TS]!	;Next task handle or 0.
		TEQ	R0, #0
		BNE	taskinit_loop2
		ORR	R4, R4, R3, LSL #16
		STR	R4, [R2]	;Store the task handle and nice time.
		STR	R0, [R2, #8]	;Activity vector = 0
		STR	R0, [R2, #12]	;(two 32-bit words).
		BL	filter_data
		SWI	XFilter_RegisterPostFilter
		BVS	loop0		;... if error.
		SWI	XOS_ReadMonotonicTime
		STR	R0, [R2, #4]	;Store monotonic time to the table.
		ADD	R5, R5, #1
		STRB	R5, [R6, #NT]	;Update the number of niced tasks.
		DBF	"--> %5#w niced task(s)\n"
		B	loop0

; ************
; Service code
; ************

		DCD	ServiceTable-BaseAddr
RM_Service	MOV	R0, R0		;Magic instruction for Ursula kernel.
		TEQ	R1, #Serv_WCD	;Service: Wimp_Closedown.
		TEQNE	R1, #Serv_FMD	;Service: Filter Manager Dying.
		MOVNE	PC, LR
RM_FastServ	TEQ	R1, #Serv_WCD
		BNE	fm_dying

; Remove the task if it is niced...

		STMFD	SP!, {R0-R4,LR}
		LDR	R12, [R12, #0]	;R12: pointer to workspace.
		MOV	R2, R2, LSL #16
		MOV	R3, R2, LSR #16	;R3: task handle (16 bits).
		MOV	R1, #MAXT	;Size of the task table.
		ADD	R2, R12, #TASKS-TS
cldn_loop	SUBS	R1, R1, #1	;Search the table for the task handle...
		LDMCCFD	SP!, {R0-R4,PC}
		LDR	R0, [R2, #TS]!
		EORS	R0, R3, R0, LSR #16
		BNE	cldn_loop

; Task found, remove it...

remove		DBF	"remove task &%3h\n"
		STR	R0, [R2]	;Store 0 to the task handle entry.
		BL	filter_data
		SWI	XFilter_DeRegisterPostFilter
		LDRB	R0, [R12, #NT]
		SUB	R0, R0, #1
		STRB	R0, [R12, #NT]	;Update the number of niced tasks.
		DBF	"--> %0#w niced task(s)\n"
		LDMFD	SP!, {R0-R4,PC}

; Filter Manager Dying: all the filters will automatically be removed.
; --> reinit the table.

fm_dying	STMFD	SP!, {R0,R2,R5,LR}
		LDR	R2, [R12]
		MOV	R0, #0
		BL	init
		LDMFD	SP!, {R0,R2,R5,PC}

; *******************
; Initialisation code
; *******************

RM_Init		DBSET	DebugOn :OR: UseTracker
		DBF	"RM_Init\n"
		LDR	R2, [R12]	;R2: pointer to workspace or 0.
		MOV	R6, LR
		TEQ	R2, #0
		BNE	init2		;Branch if re-init.
		MOV	R0, #6
		MOV	R3, #SIZE
		SWI	XOS_Module	;Claim.
		MOVVS	PC, R6		;Return with V=1 if not enough memory.
		STR	R2, [R12]	;Pointer to workspace.
init2		MOV	R0, #0
		STR	R0, [R2]	;Reset task handle.
		STR	R0, [R2, #LIST]	;Empty list of nice+ tasks.
		MOV	LR, R6
init		DBF	"reset task table\n--> 0 niced tasks\n"
		STRB	R0, [R2, #NT]	;0 niced tasks.
		ADD	R2, R2, #TASKS
		MOV	R5, #MAXT
init_loop	STR	R0, [R2], #TS	;Clear all the task handle entries.
		SUBS	R5, R5, #1
		BNE	init_loop
		MOV	PC, LR		;Return with no error.

; *****************
; Finalisation code
; *****************

RM_Die		DBF	"RM_Die\n"
		MOV	R6, LR
		LDR	R12, [R12, #0]	;R12: pointer to workspace.
		MOV	R5, #MAXT
		LDR	R0, [R12]
		LDR	R1, task	;"TASK".
		TEQ	R0, #0
		SWINE	XWimp_CloseDown
		ADD	R2, R12, #TASKS
		BL	filter_data
die_loop	LDR	R3, [R2]	;Deregister all the filters...
		MOVS	R3, R3, LSR #16	;Task handle or 0.
		SWINE	XFilter_DeRegisterPostFilter
		ADD	R2, R2, #TS
		SUBS	R5, R5, #1
		BNE	die_loop
		LDR	R2, [R12, #LIST] ;Free the nice+ list...
		MOV	R0, #7
		CMP	R2, #0		;(V = 0.)
		MOVEQ	PC, R6		;Return with no error if 0.
free_loop	LDR	R3, [R2]	;R3: pointer to next cell or 0.
		SWI	XOS_Module	;Free block R2.
		ADDS	R2, R3, #0	;(V = 0.)
		BNE	free_loop
		MOV	PC, R6		;Return with no error.

		END
