
		incpath	include:
		maclib	sm.mac
		macfile	macro.i
		macfile	snoopy.i
		macfile	macros/main
		macfile	extern/parse
		macfile	extern/misc
		macfile	macros/startup
		macfile	extern/patch

		DETACHSTARTUPARGS <argTemplate>,<argFirst>,maincode,4096

maincode	bsr	Setup
		tst	d0
		bne	.FAIL

;--------------	if the user only wants to quit, don't show anything
		tst.l	(argQuit)
		bne	.QUITONLY

;--------------	give out initial welcome to the user
		move.l	(currentInstance),(outputArgs)
		SHOWMSG	hello

;--------------	main loop ! Wait for signals to arrive, and process them 
.MainLoop	move.l	(waitsignals),d0
		CALL	Wait,<(execBase).w>
		move.l	d0,(signals)

;--------------	CTRL+C terminates the program
		BRBS	#SIGBREAKB_CTRL_C,d0,.DONE

;--------------	if we got a message, then answer it
		move.l	d0,d1
		and.l	(answersignal),d1
		tst.l	d1
		beq.b	.NoMessage
		bsr	HandleReply
		bra.b	.MainLoop
.NoMessage
;--------------	CTRL+D disables output
		BRBC.B	#SIGBREAKB_CTRL_D,d0,.DisableOutput
		tst.b	(outputactive)
		beq.b	.MainLoop
		move.b	#FALSE,(outputactive)
		SHOWMSG	disableout
		bra	.MainLoop
.DisableOutput
;--------------	CTRL+E disables output
		BRBC.B	#SIGBREAKB_CTRL_E,d0,.EnableOutput
		tst.b	(outputactive)
		bne.b	.MainLoop
		move.b	#TRUE,(outputactive)
		SHOWMSG	enableout
		bra	.MainLoop
.EnableOutput
;--------------	CTRL+F toggles the task info feature
		BRBC.B	#SIGBREAKB_CTRL_F,d0,.ToggleTI
		tst.l	(argTaskInfo)
		bne.b	.TI_Disabled
		move.l	#TRUE,(argTaskInfo)
		SHOWMSG	TIenabled
		bra	.MainLoop
.TI_Disabled	SHOWMSG	TIdisabled
		clr.l	(argTaskInfo)
.ToggleTI
		bra	.MainLoop

;--------------	sorry, calls still active
.CALLSACTIVE	lea	(activeCallsErr,pc),a0
		move.l	(ActiveCalls),(outputArgs)
		jsr	ShowErrorMessage
		bra	.MainLoop

.DONE		tst.l	(ActiveCalls)
		bne.b	.CALLSACTIVE
		bsr	RemoveFunctions	; remove all functions
		tst.w	d0
		beq	.MainLoop
.QUITONLY	bsr	Cleanup
		moveq	#RETURN_OK,d0
		rts

.FAIL		bsr	RemoveFunctions ; MUST succeed, otherwise we're really in trouble
		bsr	Cleanup
		moveq	#RETURN_ERROR,d0
		rts

***********************************************************************************
;--------------	
		ENTRY	Setup,d1-d7/a0-a6
		clr.l	(ActiveCalls)		

;--------------	empty all lists used
		prepare	hides
		prepare	shows
		prepare	bases
		prepare	watches
		prepare	deviceBases
		prepare	aliases
		prepare	defines
		prepare	resources
		move.b	#TRUE,(outputactive)

;--------------	open librarys needed
		openlib	int
		openlib	icon
		bsr	ReadWBArgs

;--------------	perhaps we only need to remove any running Snoopys ?! 
		tst.l	(argQuit)
		beq.b	.DONTQUIT
		bsr	QuitSnoopyInstances
		moveq	#TRUE,d0
		bra	Setup_done
.DONTQUIT
;--------------	build taskname to be removeable 
		bsr	BuildSnoopysTaskname

;--------------	try to open script file
		move.l	(argScript),d0
		bne.b	.CallParser
		move.l	#defaultScript,d0
.CallParser	movea.l	d0,a0
		jsr	ParseScriptFile
		tst	d0
		bne	.NODOSBASE ; this looks strange but it works just fine

;--------------	defines and aliases are not needed any longer, so they can be
;--------------	freed immediately 
		remlist	defines,sdef_SIZEOF
		remlist	aliases,salias_SIZEOF

;--------------	try to find SegTracker(tm)
		clr.l	(SegTracker)
		lea	(SegTrackerName,pc),a1
		CALL	FindSemaphore,<(execBase).w>
		tst.l	d0
		beq.b	.NOSEMAPHORE
		movea.l	d0,a0
		move.b	#TRUE,(SegTrackerOn)
		move.l	(segs_Find,a0),(SegTracker)
.NOSEMAPHORE
;--------------	create msgport
		CALL	CreateMsgPort,<(execBase).w>
		move.l	d0,(myport)
		beq	.NOMSGPORT
		move.l	(ThisTask,a6),(thistask)
		movea.l	d0,a1
		move.l	#currentPortName,(LN_NAME,a1)
		moveq	#0,d0
		move.b	(MP_SIGBIT,a1),d0
		moveq	#1,d1
		lsl.l	d0,d1
		move.l	d1,(answersignal)
		or.l	#MASK_SIGNALS,d1
		move.l	d1,(waitsignals)
		CALL	AddPort

;--------------	open CON: window
		move.l	#windowName,d1
		tst.b	(outputMemory)
		beq.b	.NOOUTPUTKEYWRD
		move.l	#outputMemory,d1
.NOOUTPUTKEYWRD	tst.l	(argOutput)
		beq.b	.DefaultWindow
		move.l	(argOutput),d1
.DefaultWindow	move.l	#MODE_READWRITE,d2
		CALL	Open,<(dosBase)>
		move.l	d0,(window)
		beq.b	.NOWINDOW
		jsr	SetCmdlineSHOWs

;--------------	patch functions
		bsr	PatchFunctions
		tst.w	d0
		beq.b	.NODOSBASE

;--------------	set my priority (if applicable)
		move.l	(priority),d0
		tst.l	(argPri)
		beq.b	.NOPRICMD
		movea.l	(argPri),a0
		move.l	(a0),d0
.NOPRICMD	movea.l	(execBase).w,a6
		movea.l	(ThisTask,a6),a1
		CALL	SetTaskPri

		moveq	#RETURN_OK,d0
		bra.b	Setup_done

.NOWINDOW	lea	(nowindowErr,pc),a0
		bra.b	.FAIL		
.NOMSGPORT	lea	(nomsgportErr,pc),a0
.FAIL		jsr	ShowErrorMessage
.NODOSBASE	moveq	#RETURN_ERROR,d0
		DONE	Setup

***********************************************************************************
;--------------	
		ENTRY	Cleanup,d0-d7/a0-a6

		bsr	RemoveDeviceBases
		bsr	RemoveLibraryBases

;--------------	close window
		move.l	(window),d1
		beq.b	.NoWindow
		CALL	Close,<(dosBase)>
.NoWindow
;--------------	remove msgport
		move.l	(myport),d0
		beq.b	.NoMsgPort
		movea.l	d0,a1
		CALL	RemPort,<(execBase).w>
		bsr	RemoveAllMessages
		movea.l	(myport),a0
		CALL	DeleteMsgPort
.NoMsgPort
;--------------	close CLI arguments
		move.l	(argRdargs),d1
		beq.b	.NoRdArgs
		CALL	FreeArgs,<(dosBase)>
.NoRdArgs
;--------------	close librarys
		closelb	int
		closelb	icon

;--------------	remove structures
		remlist	defines,sdef_SIZEOF
		remlist	aliases,salias_SIZEOF
		remlist	bases,sbase_SIZEOF
		remlist	resources,sbase_SIZEOF
		remlist	hides,stask_SIZEOF
		remlist	shows,stask_SIZEOF
		remlist	watches,spatch_SIZEOF
		remlist	deviceBases,sdevbase_SIZEOF
		DONE	Cleanup

***********************************************************************************
;--------------	remove all remaining messages from the port (so that we can be sure
;--------------	we have not lost any memory
;--------------	
		ENTRY	RemoveAllMessages,d0-d7/a0-a6

;--------------	free message memory
		movea.l	(execBase).w,a6
.LOOP		movea.l	(myport),a0
		CALL	GetMsg
		tst.l	d0
		beq.b	.DONE
		movea.l	d0,a1
		move.l	#smsg_SIZEOF,d0
		CALL	FreeMem
		bra.b	.LOOP
.DONE		DONE	RemoveAllMessages

***********************************************************************************
;--------------	remove all library bases

		ENTRY	RemoveLibraryBases,d0-d7/a0-a6
		lea	(bases),a5
		movea.l	(LH_HEAD,a5),a5
		movea.l	(execBase).w,a6
.BASELOOP	tst.l	(LN_SUCC,a5)	; a5 = current node
		beq.b	.BASEDONE
		move.l	(sbase_Library,a5),d0
		beq.b	.NEXTBASE
		clr.l	(sbase_Library,a5)
		movea.l	d0,a1
		CALL	CloseLibrary
.NEXTBASE	movea.l	(LN_SUCC,a5),a5
		bra.b	.BASELOOP
.BASEDONE	DONE	RemoveLibraryBases

***********************************************************************************
;--------------	remove all device bases

		ENTRY	RemoveDeviceBases,d0-d7/a0-a6
		lea	(deviceBases),a5
		movea.l	(LH_HEAD,a5),a5
.BASELOOP2	tst.l	(LN_SUCC,a5)	; a5 = current node
		beq.b	.BASEDONE2

;--------------	remove message port
		;move.l	(sdevbase_MsgPort,a5),d0
		;beq.b	.NOMSGPORT
		;movea.l	d0,a0
		;CALL	DeleteMsgPort,<(execBase).w>
.NOMSGPORT
;--------------	remove device bases
		move.l	(sdevbase_Device,a5),d0
		beq.b	.NEXTBASE2
		clr.l	(sdevbase_Device,a5)
		movea.l	d0,a1
		CALL	Forbid,<(execBase).w>
		subq.w	#1,(LIB_OPENCNT,a1)
		move.b	(LIB_FLAGS,a1),d0
		bset	#LIBB_CHANGED,d0
		move.b	d0,(LIB_FLAGS,a1)
		CALL	SumLibrary
		CALL	Permit

;--------------	free all patches for this device)
.NEXTBASE2	lea	(sdevbase_Patches,a5),a0
		move.l	#spatch_SIZEOF,d0
		jsr	RemoveList
		movea.l	(LN_SUCC,a5),a5
		bra.b	.BASELOOP2
.BASEDONE2	DONE	RemoveDeviceBases

***********************************************************************************
;--------------	build a unique taskname for the current copy of Snoopy [this is
;--------------	required even if option QUIT was given]
;--------------	
		ENTRY	BuildSnoopysTaskname,d0-d7/a0-a6
		moveq	#0,d7	; used to count the number of Snoopys installed

;--------------	examine waiting tasks
		CALL	Forbid,<(execBase).w>	; stop multitasking
		lea	(TaskWait,a6),a4
		movea.l	(LH_HEAD,a4),a4
.LOOP1		tst.l	(LN_SUCC,a4)
		beq.b	.DONE1
		bsr.b	.FINDSNOOPY
		movea.l	(LN_SUCC,a4),a4
		bra.b	.LOOP1

;--------------	examine ready tasks
.DONE1		lea	(TaskReady,a6),a4
		movea.l	(LH_HEAD,a4),a4
.LOOP2		tst.l	(LN_SUCC,a4)
		beq.b	.DONE2
		bsr.b	.FINDSNOOPY
		movea.l	(LN_SUCC,a4),a4
		bra.b	.LOOP2
.DONE2		CALL	Permit,<(execBase).w>	; reallow multitasking
		addq.w	#1,d7	; "addq.l" would be too pretentious, really....  ;-)
		move.l	d7,(currentInstance)

;--------------	build new taskname and portname
		lea	(outputArgs),a1		
		move.l	d7,(a1)
		lea	(taskNameFormat,pc),a0
		lea	(currentTaskName),a2
		jsr	SPrintf
		lea	(taskPortFormat,pc),a0
		lea	(currentPortName),a2
		jsr	SPrintf

;--------------	install taskname
		movea.l	(ThisTask,a6),a0
		move.l	#currentTaskName,(LN_NAME,a0)
		bra.b	BuildSnoopysTaskname_done

;--------------	trys to find out the name for a task
.FINDSNOOPY	move.l	(LN_NAME,a4),d0
		beq.b	.SKIP
		movea.l	d0,a0
		lea	(taskNameFormat,pc),a1
		moveq	#taskNameFormat_SIZEOF-1,d0
.STRCMP		move.b	(a0)+,d1
		cmp.b	(a1)+,d1
		bne.b	.SKIP
		dbra	d0,.STRCMP
		moveq	#STVFORMAT_DEC,d0
		jsr	StringToValue
		cmp.w	d7,d0
		bls.b	.SKIP
		move.w	d0,d7
.SKIP		rts

		DONE	BuildSnoopysTaskname

***********************************************************************************
;--------------	yeah, we got ourselfs a message; now show it and go home
;--------------	
		ENTRY	HandleReply,d0-d7/a0-a6

.LOOP		movea.l	(myport),a0
		CALL	GetMsg,<(execBase).w>
		move.l	d0,(message)
		tst.l	d0
		beq	HandleReply_done
		movea.l	d0,a5			; a5=message

;--------------	output handling is different if its a device message
		move.w	(smsg_Type,a5),d0
		BRBS	#MSGTYPEB_DEBUG,d0,.HANDLEDEBUG
		BRBS	#MSGTYPEB_DEVICE,d0,.HANDLEDEVICE

;--------------	now we have the message and its data, so lets go for it !
		tst.b	(outputactive)
		beq	.SkipOutput

;--------------	if the task is hidden, skip output
		movea.l	(smsg_Task,a5),a0
		bsr	IsTaskHidden
		tst	d0
		bne.b	.SkipOutput

;--------------	handle 
		movea.l	(smsg_Info,a5),a0
		move.l	(spatch_Flags,a0),d0
		BRBC.B	#SINFOB_SKIPSIMILAR,d0,.SKIPSIMILAR
		move.l	a0,d0
		cmp.l	(lastMessage),d0
		beq.b	.SkipOutput
		move.l	d0,(lastMessage)
.SKIPSIMILAR	
;--------------	show extended task info if applicable
		tst.l	(argTaskInfo)
		beq.b	.NoTaskInfo
		movea.l	(smsg_Task,a5),a0
		bsr	ShowTaskInfo
.NoTaskInfo
;--------------	if SEGTRACKER is there, show segment of pc
		tst.l	(SegTracker)
		beq.b	.NoSegTracker
		movea.l	(smsg_RegsBeforeCall+sregs_I7,a5),a0	; a0=address to show
		movea.l	(smsg_Info,a5),a1			; a1=PatchInfo
		move.l	(spatch_Flags,a1),d0
		BRBS.B	#SINFOB_NOSEGTRACKER,d0,.NoSegTracker
		bsr	ShowSegTracker
.NoSegTracker
;--------------	prepare output data
.SHOWMESSAGE	movea.l	(smsg_Info,a5),a4	; a4=PatchInfo
		lea	(spatch_Arguments,a4),a1	; a1 = current position in argument template
		moveq	#TRUE,d0
		bsr	SetupOutputData

;--------------	send message
		lea.l	(spatch_Template,a4),a0
		jsr	ShowMessage
		move.l	(window),d1
		CALL	Flush,<(dosBase)>
		bsr.b	.NEWLINE

;--------------	free message memory
.SkipOutput	movea.l	(message),a1
		move.l	#smsg_SIZEOF,d0
		CALL	FreeMem,<(execBase).w>
		bra	.LOOP

.NEWLINE	move.l	(window),d1
		move.l	#newlineMsg,d2
		moveq	#1,d3
		CALL	Write,<(dosBase)>
		move.l	(window),d1
		CALL	Flush
		rts

;---------------------------------------------------------------------------
;--------------	this is were we deal with USER-DEBUG messages
;--------------	

;--------------	skip if output deactivated
.HANDLEDEBUG	tst.b	(outputactive)
		beq	.LISTDONE

;--------------	if the task is hidden, skip output
		movea.l	(message),a5
		movea.l	(sumsg_Task,a5),a0
		bsr	IsTaskHidden
		tst	d0
		bne	.LISTDONE

;--------------	show extended task info if applicable
		tst.l	(argTaskInfo)
		beq.b	.NoDebugTaskInfo
		movea.l	(sumsg_Task,a5),a0
		bsr	ShowTaskInfo
.NoDebugTaskInfo
;--------------	if SEGTRACKER is there, show segment of pc
		tst.l	(SegTracker)
		beq.b	.NoDebugSegTracker
		movea.l	(sumsg_RegsBeforeCall+sregs_I7,a5),a0	; a0=address to show
;--------------	SKIPSEGTRACKER not supported for Debug calls
		bsr	ShowSegTracker
.NoDebugSegTracker
		move.l	(sumsg_Template,a5),d0
		beq	.DebugUseDefaultTemplate
		movea.l	d0,a4
		lea	(4,a4),a1	; a1 = current position in argument template
		bsr	SetupOutputData
		moveq	#FALSE,d0
		bsr	SetupOutputData

;--------------	send message
		movea.l	(sumsg_Template,a5),a0
		movea.l	(a0),a0
		jsr	ShowMessage
		move.l	(window),d1
		CALL	Flush,<(dosBase)>
		bsr	.NEWLINE
		bra	.LISTDONE

;--------------	no explicit user template, use default
.DebugUseDefaultTemplate
		lea	(outputArgs),a0
		lea	(sumsg_RegsBeforeCall,a5),a1
		SAVEDEBUGREGISTERS
		lea	(sumsg_RegsAfterCall,a5),a1
		SAVEDEBUGREGISTERS
		lea	(DebugDefTempMsg,pc),a0
		jsr	ShowMessage
		move.l	(window),d1
		CALL	Flush,<(dosBase)>
		bra	.LISTDONE

;---------------------------------------------------------------------------
;--------------	the rest of this function deals with the device messages
;--------------	

;--------------	skip if output deactivated
.HANDLEDEVICE	tst.b	(outputactive)
		beq	.LISTDONE

;--------------	if the task is hidden, skip output
		movea.l	(message),a4
		movea.l	(sdmsg_Task,a4),a0
		bsr	IsTaskHidden
		tst	d0
		bne	.LISTDONE

;--------------	lets see if we understand this message
		movea.l	(sdmsg_DeviceBase,a4),a5
		lea	(sdevbase_Patches,a5),a5
		movea.l	(LH_HEAD,a5),a5			; a5=current patch
		move.w	(sdmsg_SIZEOF+IO_COMMAND,a4),d0	; d0=current command
.LISTLOOP	tst.l	(LN_SUCC,a5)
		beq.b	.LISTDONE

;--------------	offset=-1 -> show always
		cmpi.w	#-1,(spatch_Offset,a5)
		beq.b	.SHOWALWAYS
		cmp.w	(spatch_Offset,a5),d0
		bne.b	.SKIPLIST

;--------------	show only if the appropriate bits are set
.SHOWALWAYS	move.l	(spatch_Flags,a5),d0
		move.w	(smsg_Type,a4),d1
		BRBS.B	#MSGTYPEB_BEGINIO,d1,.ITSBEGINIO
		BRBC.B	#SINFOB_ABORTIO,d0,.SKIPLIST
		bra.b	.ITSOK
.ITSBEGINIO	BRBC.B	#SINFOB_BEGINIO,d0,.SKIPLIST
.ITSOK
;--------------	show extended task info if applicable
		tst.l	(argTaskInfo)
		beq.b	.NoDTaskInfo
		movea.l	(sdmsg_Task,a4),a0
		bsr	ShowTaskInfo
;--------------	if SEGTRACKER is there, show segment of pc
.NoDTaskInfo	tst.l	(SegTracker)
		beq.b	.NoDSegTracker
		movea.l	(sdmsg_PC,a4),a0			; a0=address to show
		move.l	(spatch_Flags,a5),d0
		BRBS.B	#SINFOB_NOSEGTRACKER,d0,.NoDSegTracker
		bsr	ShowSegTracker
.NoDSegTracker	movea.l	a5,a0
		bsr.b	ShowDeviceMessage
		bsr	.NEWLINE
		bra.b	.LISTDONE
.SKIPLIST	movea.l	(LN_SUCC,a5),a5
		bra.b	.LISTLOOP

;--------------	free message memory and return to main loop
.LISTDONE	movea.l	(message),a1
		moveq	#0,d0
		move.w	(MN_LENGTH,a1),d0
		CALL	FreeMem,<(execBase).w>
		bra	.LOOP

		DONE	HandleReply

***********************************************************************************
;--------------	
;--------------	=> a0: (struct SnoopyPatch *)
;--------------	
		ENTRY	ShowDeviceMessage,d0-d7/a0-a6

;--------------	prepare output data
		movea.l	(message),a5
		movea.l	a0,a4	; a4 = patch 
		bsr	SetupDeviceOutputData

;--------------	send message
		lea.l	(spatch_Template,a4),a0
		jsr	ShowMessage
		move.l	(window),d1
		CALL	Flush,<(dosBase)>
		DONE	ShowDeviceMessage

***********************************************************************************
;--------------	
;--------------	-> a4: Patch Info
;--------------	   a5: Register Data

		ENTRY	SetupDeviceOutputData,d0-d7/a0-a6
		lea	(outputArgs),a0	; a0 = current output offset
		lea	(spatch_Arguments,a4),a1	; a1 = current position in argument template
		movea.l	(message),a3
		lea	(sdmsg_SIZEOF,a3),a2

;--------------	done ? then lets go !
.LOOP		move.w	(sarg_Opcode,a1),d0
		cmpi.w	#REG_DONE,d0
		beq.b	SetupDeviceOutputData_done

;--------------	if REGB_INDIRECT is given, I display internal data!!!!!!!!!
		BRBS	#REGB_INDIRECT,d0,.SHOWINTERNAL
		BRBS.B	#REGB_BYTE,d0,.SETBYTE
		BRBS.B	#REGB_WORD,d0,.SETWORD
.SETLONG	move.l	(sarg_Offset,a1),d0
		move.l	(a2,d0.l),(a0)+
		bra.b	.NEXT
.SETWORD	move.l	(sarg_Offset,a1),d0
		move.w	(a2,d0.l),(a0)+
		bra.b	.NEXT
.SETBYTE	move.l	(sarg_Offset,a1),d0
		move.b	(a2,d0.l),d0
		ext.w	d0
		move.w	d0,(a0)+
.NEXT		lea	(sarg_SIZEOF,a1),a1
		bra.b	.LOOP

;--------------	
.SHOWINTERNAL	andi.w	#%111,d0	; get bit number
		tst.b	d0
		beq.b	.SHOWCALLTYPE

;--------------	NOT SUPPORTED!
		bra.b	.NEXT

.SHOWCALLTYPE	move.l	#abortIOMsg,d0
		move.w	(smsg_Type,a3),d1
		BRBC.B	#MSGTYPEB_BEGINIO,d1,.ISABORTIO
		move.l	#beginIOMsg,d0
.ISABORTIO	move.l	d0,(a0)+
		bra.b	.NEXT
		
		DONE	SetupDeviceOutputData

***********************************************************************************
;--------------	checks if the current task is hidden, returns TRUE if so
		ENTRY	IsTaskHidden,d1-d7/a0-a6

;--------------	a3 = task pointer
		movea.l	a0,a3

;--------------	find taskname to a4
		movea.l	a0,a1
		cmpi.b	#NT_PROCESS,(LN_TYPE,a0)
		bne.b	.DEFAULT
		tst.l	(pr_TaskNum,a0)
		beq.b	.DEFAULT
		move.l	(pr_CLI,a0),d0
		lsl.l	#2,d0
		movea.l	d0,a0
		move.l	(cli_CommandName,a0),d0
		lsl.l	#2,d0
		addq.l	#1,d0
		movea.l	d0,a4
		bra.b	.DONE
.DEFAULT	movea.l	(LN_NAME,a0),a4
.DONE	
;--------------	handle SHOWs
		tst.b	(showsactive)
		beq.b	.NoSHOWs
		lea	(shows),a5
		bsr.b	CheckIfIsInList
		not.l	d0
		bra.b	IsTaskHidden_done
.NoSHOWs
;--------------	handle HIDEs
		lea	(hides),a5
		bsr.b	CheckIfIsInList
		DONE	IsTaskHidden

***********************************************************************************
;--------------	checks if an entry can be found in a list
;--------------	
;-------------- -> a5: struct List *HEAD
;--------------	   a4: STRPTR name
;--------------	   a3: APTR task
;--------------	<- d0: TRUE=Member of this list, FALSE otherwise
;--------------	
		ENTRY	CheckIfIsInList,d1-d7/a0-a6
		cmpa.l	#0,a4
		beq.b	.NotInList
		movea.l	(LH_HEAD,a5),a5
.LOOP		tst.l	(LN_SUCC,a5)	; a5 = current node
		beq.b	.NotInList
		movea.l	a4,a0
		move.l	(stask_Pointer,a5),d0
		beq.b	.CHECKNAME
		cmpa.l	d0,a3
		beq.b	.InList
.CHECKNAME	lea	(stask_Name,a5),a1

;--------------	NEW: exact matching enabled
		tst.l	(argMatch)
		bne.b	.STRCMP

.STRICMP	move.b	(a1)+,d0
		beq.b	.InList
		move.b	(a0)+,d1
		UCASE	d0
		UCASE	d1
		cmp.b	d1,d0
		beq.b	.STRICMP
		movea.l	(LN_SUCC,a5),a5
		bra.b	.LOOP
.NotInList	moveq	#0,d0
		bra.b	CheckIfIsInList_done

;--------------	NEW: enable exact matching
.STRCMP		move.b	(a1)+,d0
		beq.b	.SEEMSTOMATCH
		move.b	(a0)+,d1
		cmp.b	d1,d0
		beq.b	.STRCMP
		movea.l	(LN_SUCC,a5),a5
		bra.b	.LOOP
.SEEMSTOMATCH	tst.b	(a0)+
		bne.b	.LOOP
.InList		moveq	#-1,d0
		DONE	CheckIfIsInList

***********************************************************************************
;--------------	show an information line about the task that called this function
;--------------	
		ENTRY	ShowTaskInfo,d1-d7/a0-a6
		lea	(taskinfoMsg),a1
		move.l	a0,(outputArgs)
		cmpi.b	#NT_PROCESS,(LN_TYPE,a0)
		bne.b	.DEFAULT
		tst.l	(pr_TaskNum,a0)
		beq.b	.DEFAULT
		move.l	(pr_CLI,a0),d0
		lsl.l	#2,d0
		movea.l	d0,a0
		move.l	(cli_CommandName,a0),(outputArgs+4)
		lea	(taskbcplMsg),a1
		bra.b	.DONE
.DEFAULT	move.l	(LN_NAME,a0),(outputArgs+4)
.DONE		move.l	a1,a0

;--------------	new: if SegTracker is enabled also, combine both outputs
		tst.l	(SegTracker)
		beq.b	.SHOWMESSAGE
		move.l	(outputArgs),(st_SegTaskAddr)
		move.l	(outputArgs+4),(st_SegFilename)
		bra.b	ShowTaskInfo_done

.SHOWMESSAGE	jsr	ShowMessage
		move.l	(window),d1
		CALL	Flush,<(dosBase)>
		DONE	ShowTaskInfo

***********************************************************************************
;--------------	
;--------------	=> a0: APTR address to show
		ENTRY	ShowSegTracker,d0-d7/a0-a6
		move.l	a0,d7				; address to show

;--------------	the manual says we have to do this, so we do it 
;--------------	(although we really couldn't care less, I tell you)
		CALL	Forbid,<(execBase).w>

;--------------	call SegTracker to find Hunk & Offset
		movea.l	d7,a0
		lea	(st_SegHunk),a1
		lea	(st_SegOffset),a2
		movea.l	(SegTracker),a6
		jsr	a6
		tst.l	d0
		beq	.NOTFOUND

;--------------	name found, copy it to our internal buffer
		movea.l	d0,a0
		lea	(errorMsgBuffer),a1
		move.l	a1,(st_SegName)
		moveq	#128-1,d0
.STRCPY		move.b	(a0)+,(a1)+
		beq.b	.STRCPYEND
		dbra	d0,.STRCPY
.STRCPYEND	move.l	d7,(st_SegAddress)

;--------------	lets see if we can find the address
		movea.l	d7,a0
		lea	(st_SegList),a1
		movea.l	a1,a2
		movea.l	(SegTracker),a6
		jsr	a6

		move.l	(st_SegList),d0
		move.l	(st_SegHunk),d1
		cmp.l	d0,d1
		bne.b	.USESEGLIST
		move.l	(st_SegOffset),d1
		cmp.l	d0,d1
		bne.b	.USESEGLIST
		clr.l	(st_SegList)
.USESEGLIST
;--------------	display the message
		move.l	(window),d1
		move.l	#SegTrackerMsg,d2
		tst.l	(argTaskInfo)
		beq.b	.NOTASKINFO
		move.l	#SegTrackerMsgTI,d2
.NOTASKINFO	move.l	#st_SegAddress,d3
		CALL	VFPrintf,<(dosBase)>
		move.l	(window),d1
		CALL	Flush
		bra.b	.DONE

;--------------	show warning
.NOTFOUND	tst.l	(flagSkipSegNotFound)
		bne.b	.DONE
		lea	(SegTrackerFail,pc),a0
		jsr	ShowMessage
		move.l	(window),d1
		CALL	Flush,<(dosBase)>
.DONE		CALL	Permit,<(execBase).w>
		DONE	ShowSegTracker

***********************************************************************************
;--------------	setup outputArgs to contain the registers as specified by 
;--------------	the register template
;--------------	
;--------------	-> a4: Patch Info
;--------------	   a5: Register Data
;--------------	   d0: BOOL type
		ENTRY	SetupOutputData,d0-d7/a0-a6
		lea	(outputArgs),a0	; a0 = current output offset
		move.w	d0,d7

;--------------	done ? then lets go !
.LOOP		move.w	(sarg_Opcode,a1),d0
		cmpi.w	#REG_DONE,d0
		beq	SetupOutputData_done

;--------------	set correct register data to a2
		tst.w	d7
		beq.b	.DebugRegList
		lea	(smsg_RegsBeforeCall,a5),a2 ; a2 = regs before call ( default )
		BRBC.B	#REGB_RESULT,d0,.BeforeCall
		lea	(smsg_RegsAfterCall,a5),a2 ; a2 = regs before call ( default )
		bclr	#REGB_RESULT,d0
.BeforeCall	bra.b	.DebugRegListDone
.DebugRegList	lea	(sumsg_RegsBeforeCall,a5),a2 ; a2 = regs before call ( default )
		BRBC.B	#REGB_RESULT,d0,.DebugRegListDone
		lea	(sumsg_RegsAfterCall,a5),a2 ; a2 = regs before call ( default )
		bclr	#REGB_RESULT,d0
.DebugRegListDone


;--------------	use boolean tables if applicable
		BRBS.B	#REGB_BOOLEAN,d0,.UseBOOLEAN

;--------------	set correct jump table to a3
		bclr	#REGB_WORD,d0
		lea	(.JumpWORD,pc),a3
		BRBC.B	#REGB_LONG,d0,.NotLONG
		lea	(.JumpLONG,pc),a3
		bclr	#REGB_LONG,d0
.NotLONG	BRBC.B	#REGB_BYTE,d0,.NotBYTE
		lea	(.JumpBYTE,pc),a3
		bclr	#REGB_BYTE,d0
.NotBYTE
;--------------	find correct table offset
.ShowOutput	BRBS.B	#REGB_DATA,d0,.ISDATA
		BRBC.B	#REGB_ADDR,d0,.ISADDR
		lea	(8*4,a3),a3
		bra.b	.ISDATA
.ISADDR		BRBC.B	#REGB_INDIRECT,d0,.ISDATA
		lea	(2*8*4,a3),a3
.ISDATA
;--------------	now get the register data and save it
		andi.w	#%111,d0
		lsl.w	#2,d0
		movea.l	(a3,d0.w),a3
		jsr	a3
		tst.w	d7
		bne.b	.ITSAPATCH
.ITSADEBUGMSG	lea	(2,a1),a1
		bra	.LOOP

.ITSAPATCH	lea	(sarg_SIZEOF,a1),a1
		bra	.LOOP

;--------------	this isn't really 'clean code', but well it works
.UseBOOLEAN	bclr	#REGB_WORD,d0
		lea	(.JumpWORDBOOL,pc),a3
		BRBC.B	#REGB_LONG,d0,.NotLONGBOOL
		lea	(.JumpLONGBOOL,pc),a3
		bclr	#REGB_LONG,d0
.NotLONGBOOL	BRBC.B	#REGB_BYTE,d0,.NotBYTEBOOL
		lea	(.JumpBYTEBOOL,pc),a3
		bclr	#REGB_BYTE,d0
.NotBYTEBOOL	bclr	#REGB_BOOLEAN,d0
		bra.b	.ShowOutput

		REGLIST	LONG,L
		REGLIST	WORD,W
		REGLIST	BYTE,B
		REGLIST	LONGBOOL,LB
		REGLIST	WORDBOOL,WB
		REGLIST	BYTEBOOL,BB

		SAVEREG	A0
		SAVEREG	A1
		SAVEREG	A2
		SAVEREG	A3
		SAVEREG	A4
		SAVEREG	A5
		SAVEREG	D0
		SAVEREG	D1
		SAVEREG	D2
		SAVEREG	D3
		SAVEREG	D4
		SAVEREG	D5
		SAVEREG	D6
		SAVEREG	D7
		SAVEREG I0
		SAVEREG I1
		SAVEREG I2
		SAVEREG I3
		SAVEREG I4
		SAVEREG I5
		SAVEREG I6
		SAVEREG I7

.LONGBOOLEAN	move.l	#SuccessMsg,d1
		tst.l	d0
		bne.b	.LONGISTRUE
		move.l	#FailureMsg,d1
.LONGISTRUE	move.l	d1,(a0)+
		rts

.WORDBOOLEAN	move.l	#SuccessMsg,d1
		tst.w	d0
		bne.b	.WORDISTRUE
		move.l	#FailureMsg,d1
.WORDISTRUE	move.l	d1,(a0)+
		rts

		DONE	SetupOutputData

***********************************************************************************
;--------------	patch all functions in the watch list
;--------------	
		ENTRY	PatchFunctions,d1-d7/a0-a6

;--------------	1.PASS: patch all library functions
		lea	(watches),a5
		movea.l	(LH_HEAD,a5),a5
		movea.l	(execBase).w,a6
.LOOPPASS1	tst.l	(LN_SUCC,a5)	; a5 = current node
		beq.b	.DONEPASS1
		movea.l	a5,a0
		bsr.b	InstallPatch
		tst	d0
		beq.b	.LIBFAILED
		movea.l	(LN_SUCC,a5),a5
		bra.b	.LOOPPASS1

;--------------	2.PASS: patch all device functions
.DONEPASS1	lea	(deviceBases),a5
		movea.l	(LH_HEAD,a5),a5
		movea.l	(execBase).w,a6
.LOOPPASS2	tst.l	(LN_SUCC,a5)	; a5 = current node
		beq.b	.DONEPASS2

;--------------	patch BeginIO()
		movea.l	a5,a0
		lea	(BeginIO_Patch),a1
		lea	(BeginIO_Jump),a2
		move.l	(BeginIO_SIZEOF),d0
		move.w	#DEV_BEGINIO,d1
		bsr	InstallDevicesPatch
		tst	d0
		beq.b	.DEVFAILED

;--------------	patch AbortIO()
		movea.l	a5,a0
		lea	(AbortIO_Patch),a1
		lea	(AbortIO_Jump),a2
		move.l	(AbortIO_SIZEOF),d0
		move.w	#DEV_ABORTIO,d1
		bsr	InstallDevicesPatch
		tst	d0
		beq.b	.DEVFAILED

		movea.l	(LN_SUCC,a5),a5
		bra.b	.LOOPPASS2
.DONEPASS2	moveq	#TRUE,d0
		bra.b	PatchFunctions_done

.DEVFAILED	clr.l	(sdevbase_IOSize,a5)
		moveq	#FALSE,d0
		bra.b	PatchFunctions_done

.LIBFAILED	clr.l	(spatch_Library,a5) ; to indicate failed patch
		moveq	#FALSE,d0
		DONE	PatchFunctions

***********************************************************************************
;--------------	Patch a system function
;-------------- 
;--------------	=> a0: APTR Patchinfo 
;--------------	<= d0: LONG success
;--------------	
		ENTRY	InstallPatch,d1-d7/a0-a6
		movea.l	a0,a5
		move.l	a0,(PatchInfoStruct)

;--------------	allocate patch duplicate
		move.l	(Patch_SIZEOF),d0
		move.l	#MEMF_PUBLIC|MEMF_CLEAR,d1
		CALL	AllocMem,<(execBase).w>
		tst.l	d0
		beq.b	.FAIL
		movea.l	d0,a4

;--------------	setup original vector
		movea.l	(spatch_Library,a5),a1
		move.w	(spatch_Offset,a5),d1
		addq.w	#2,d1
		move.l	(a1,d1.w),(OriginalVector0+2)
		move.l	(a1,d1.w),(OriginalVector1+2)
		move.l	(a1,d1.w),(Patch_START+sphead_OriginalFunction)

;--------------	this is VERY important because otherwise Snoopy couldn't
;--------------	handle multiple instances so well 
		move.l	(ThisTask,a6),(Patch_START+sphead_Owner)

;--------------	copy patch memory
		movea.l	a4,a0
		lea	(Patch_START),a1
		move.l	(Patch_SIZEOF),d0
		subq.w	#1,d0
.LOOP		move.b	(a1)+,(a0)+
		dbra	d0,.LOOP

;--------------	install new function
		movea.l	(spatch_Library,a5),a1
		move.w	(spatch_Offset,a5),d0
		ext.l	d0
		movea.l	d0,a0
		move.l	a4,d0
		addi.l	#sphead_SIZEOF,d0
		CALL	SetFunction
		
.SUCCESS	moveq	#TRUE,d0
		bra.b	InstallPatch_done
		
.FAIL		moveq	#FALSE,d0
		DONE	InstallPatch

***********************************************************************************
;--------------	Patch a system function
;-------------- 
;--------------	=> a0: APTR Patchinfo 
;--------------	<= d0: LONG success
;--------------	
		STRUCTURE idpStack,0
		APTR	idps_DeviceBase
		APTR	idps_BeginOfPatch
		APTR	idps_JumpAddress
		APTR	idps_SizeOfPatch
		WORD	idps_Offset
		LABEL	idps_SIZEOF

		ENTRY	InstallDevicesPatch,d1-d7/a0-a6,idps_SIZEOF,a5
		move.l	a0,(idps_DeviceBase,a5)
		move.l	a1,(idps_BeginOfPatch,a5)
		move.l	a2,(idps_JumpAddress,a5)
		move.l	d0,(idps_SizeOfPatch,a5)
		move.w	d1,(idps_Offset,a5)

;--------------	allocate patch duplicate
		move.l	(idps_SizeOfPatch,a5),d0
		move.l	#MEMF_PUBLIC|MEMF_CLEAR,d1
		CALL	AllocMem,<(execBase).w>
		tst.l	d0
		beq.b	.FAIL
		movea.l	d0,a4			; a4 = patch memory

;--------------	setup original vector
		movea.l	(idps_DeviceBase,a5),a3	; a3 = (SnoopyDeviceBase *)
		movea.l	(sdevbase_Device,a3),a1	; a1 = (struct Device *)
		move.w	(idps_Offset,a5),d1	; d1 = (function offset)
		addq.w	#2,d1	
		movea.l	(idps_JumpAddress,a5),a0
		move.l	(a1,d1.w),(2,a0)	; set jump address
		movea.l	(idps_BeginOfPatch,a5),a0
		move.l	(a1,d1.w),(sphead_OriginalFunction,a0)
		move.l	(sdevbase_IOSize,a3),(sphead_Info,a0)
		move.l	a3,(sphead_DevicePtr,a0)

;--------------	copy patch memory
		movea.l	a4,a0
		movea.l	(idps_BeginOfPatch,a5),a1
		move.l	(ThisTask,a6),(sphead_Owner,a1)
		move.l	(idps_SizeOfPatch,a5),d0
		subq.w	#1,d0
.LOOP		move.b	(a1)+,(a0)+
		dbra	d0,.LOOP

;--------------	install new function
		movea.l	(sdevbase_Device,a3),a1
		move.w	(idps_Offset,a5),d0
		ext.l	d0
		movea.l	d0,a0
		move.l	a4,d0
		addi.l	#sphead_ExtSIZEOF,d0
		CALL	SetFunction
.SUCCESS	moveq	#TRUE,d0
		bra.b	InstallDevicesPatch_done
		
.FAIL		moveq	#FALSE,d0
		DONE	InstallDevicesPatch

***********************************************************************************
;--------------	remove the patch of all functions in the watch list
;--------------	
		ENTRY	RemoveFunctions,d1-d7/a0-a6
		lea	(watches),a5
		movea.l	(LH_HEAD,a5),a5
		movea.l	(execBase).w,a6
.LOOP1		tst.l	(LN_SUCC,a5)	; a5 = current node
		beq.b	.DONE1
		movea.l	a5,a0
		bsr	RemovePatch
		tst.w	d0
		beq.b	.FAILURE
		movea.l	(LN_SUCC,a5),a5
		bra.b	.LOOP1
.DONE1
		lea	(deviceBases),a5
		movea.l	(LH_HEAD,a5),a5
.LOOP2		tst.l	(LN_SUCC,a5)	; a5 = current node
		beq.b	.DONE2
		movea.l	a5,a0

;--------------	unpatch BeginIO()
		movea.l	a5,a0
		lea	(BeginIO_Patch),a1
		lea	(BeginIO_Jump),a2
		move.l	(BeginIO_SIZEOF),d0
		move.w	#DEV_BEGINIO,d1
		bsr	RemoveDevicePatch
		tst	d0
		beq.b	.BEGINIOERR

;--------------	unpatch AbortIO()
		movea.l	a5,a0
		lea	(AbortIO_Patch),a1
		lea	(AbortIO_Jump),a2
		move.l	(AbortIO_SIZEOF),d0
		move.w	#DEV_ABORTIO,d1
		bsr	RemoveDevicePatch
		tst	d0
		beq.b	.ABORTIOERR

		movea.l	(LN_SUCC,a5),a5
		bra.b	.LOOP2
.DONE2		moveq	#TRUE,d0
		bra.b	RemoveFunctions_done

;--------------	error in DEV_BEGINIO()
.BEGINIOERR	move.l	#closeBeginIoErr,d2
		bra.b	.DEVICEERROR

;--------------	error in DEV_ABORTIO()
.ABORTIOERR	move.l	#closeAbortIoErr,d2
.DEVICEERROR	movea.l	(sdevbase_Device,a5),a1
		movea.l	(LN_NAME,a1),a1
		bra.b	.SHOWERRORMSG

;--------------	show error messages
.FAILURE	lea	(spatch_Template,a5),a1
		move.l	#closesnoopyErr,d2
.SHOWERRORMSG	move.l	a1,(outputArgs)
		move.l	(window),d1
		move.l	#outputArgs,d3
		CALL	VFPrintf,<(dosBase)>
		move.l	(window),d1
		CALL	Flush
		moveq	#FALSE,d0
		DONE	RemoveFunctions

***********************************************************************************
;--------------	Remove patch of a system function
;-------------- 
;--------------	=> a0: APTR Patchinfo 
;--------------	
		ENTRY	RemovePatch,d1-d7/a0-a3/a5/a6
		movea.l	a0,a5

;--------------	test if it is our patch
		tst.l	(spatch_Library,a5)
		beq	.SUCCESS
		movea.l	(spatch_Library,a5),a1
		move.w	(spatch_Offset,a5),d1
		addq.w	#2,d1
		move.l	(a1,d1.w),a1
		subi.l	#sphead_SIZEOF,a1
		cmpi.l	#PATCH_IDSTRING,(sphead_Ident,a1)
		bne.b	.REMOVEFAILED
		movea.l	(execBase).w,a6
		move.l	(ThisTask,a6),d0
		cmp.l	(sphead_Owner,a1),d0
		bne.b	.REMOVEFAILED
		movea.l	a1,a4			; a4 = patched memory
		tst.l	(sphead_OriginalFunction,a4)
		beq.b	.SUCCESS

;--------------	restore original function
		movea.l	(spatch_Library,a5),a1
		move.w	(spatch_Offset,a5),d0
		ext.l	d0
		movea.l	d0,a0
		move.l	(sphead_OriginalFunction,a4),d0
		CALL	SetFunction,<(execBase).w>
		clr.l	(sphead_OriginalFunction,a4)

;--------------	free patched memory
		movea.l	a4,a1
		move.l	(Patch_SIZEOF),d0
		CALL	FreeMem
		
.SUCCESS	moveq	#TRUE,d0
		bra.b	RemovePatch_done

;--------------	
.REMOVEFAILED	moveq	#FALSE,d0
		DONE	RemovePatch

***********************************************************************************
;--------------	Remove patch of a system device
;-------------- 
;--------------	=> a0: SnoopyDeviceBase *
;--------------	
		ENTRY	RemoveDevicePatch,d1-d7/a0-a6,idps_SIZEOF,a5
		move.l	a0,(idps_DeviceBase,a5)
		move.l	a1,(idps_BeginOfPatch,a5)
		move.l	a2,(idps_JumpAddress,a5)
		move.l	d0,(idps_SizeOfPatch,a5)
		move.w	d1,(idps_Offset,a5)

;--------------	test if it is our patch
		movea.l	(idps_DeviceBase,a5),a3	; a3 = SnoopyDeviceBase *
		movea.l	(sdevbase_Device,a3),a1
		move.w	(idps_Offset,a5),d1
		addq.w	#2,d1
		movea.l	(a1,d1.w),a1
		suba.l	#sphead_ExtSIZEOF,a1
		cmpi.l	#PATCH_IDSTRING,(sphead_Ident,a1)
		bne.b	.REMOVEFAILED
		movea.l	(execBase).w,a6
		move.l	(ThisTask,a6),d0
		cmp.l	(sphead_Owner,a1),d0
		bne.b	.REMOVEFAILED
		move.l	a1,a4			; a4 = patched memory
		tst.l	(sphead_OriginalFunction,a4)
		beq.b	.SUCCESS

;--------------	restore original function
		movea.l	(sdevbase_Device,a3),a1
		move.w	(idps_Offset,a5),d0
		ext.l	d0
		movea.l	d0,a0
		move.l	(sphead_OriginalFunction,a4),d0
		CALL	SetFunction,<(execBase).w>
		clr.l	(sphead_OriginalFunction,a4)

;--------------	free patched memory
		movea.l	a4,a1
		move.l	(idps_SizeOfPatch,a5),d0
		CALL	FreeMem
		
.SUCCESS	moveq	#TRUE,d0
		bra.b	RemoveDevicePatch_done

;--------------	
.REMOVEFAILED	moveq	#FALSE,d0
		DONE	RemoveDevicePatch

***********************************************************************************
;--------------	remove any running instance of Snoopy
;--------------	
		ENTRY	QuitSnoopyInstances,d0-d7/a0-a6

;--------------	d7 = Instance Number; if d7 == 0, remove ALL instances,
;--------------	else remove only the numbered instance
		movea.l	(argQuit),a0
		move.l	(a0),d7
		beq	.REMOVEALL

;--------------	stop fooling around
		CALL	Forbid,<(execBase).w>

;--------------	examine waiting tasks
		lea	(TaskWait,a6),a4
		movea.l	(LH_HEAD,a4),a4
.LOOP1		tst.l	(LN_SUCC,a4)
		beq.b	.DONE1
		bsr.b	.FINDSNOOPY
		bsr.b	.REMTASK
		movea.l	(LN_SUCC,a4),a4
		bra.b	.LOOP1

;--------------	examine ready tasks
.DONE1		lea	(TaskReady,a6),a4
		movea.l	(LH_HEAD,a4),a4
.LOOP2		tst.l	(LN_SUCC,a4)
		beq.b	.DONE2
		bsr.b	.FINDSNOOPY
		bsr.b	.REMTASK
		movea.l	(LN_SUCC,a4),a4
		bra.b	.LOOP2
.DONE2		CALL	Permit
		bra	QuitSnoopyInstances_done

;--------------	remove task if it fits 
.REMTASK	tst.w	d1
		beq.b	.FAILREMTASK
		cmp.w	d7,d1
		bne.b	.FAILREMTASK
 		movea.l	a4,a1
		move.l	#SIGBREAKF_CTRL_C,d0
		CALL	Signal
.FAILREMTASK	rts

;--------------	trys to find out the name for a task
;--------------	
;--------------	<= d0:BOOL success
;--------------	   d1:LONG instance number of the task currently found
.FINDSNOOPY	movea.l	(LN_NAME,a4),a0
		lea	(taskNameFormat,pc),a1
		moveq	#taskNameFormat_SIZEOF-1,d0
.STRCMP		move.b	(a0)+,d1
		cmp.b	(a1)+,d1
		bne.b	.SKIP
		dbra	d0,.STRCMP
		moveq	#STVFORMAT_DEC,d0
		jsr	StringToValue
		move.l	d0,d1
		moveq	#TRUE,d0
		rts
.SKIP		moveq	#FALSE,d0
		rts

MAX_ACTIVESNOOPYS	equ	MAX_INPUT

;--------------	find all active snoopy numbers and store them in InputMemory[]
.REMOVEALL	lea	(inputMemory),a3
		CALL	Forbid,<(execBase).w>
		lea	(TaskWait,a6),a4
		bsr.b	.FINDSNOOPYS
		lea	(TaskReady,a6),a4
		bsr.b	.FINDSNOOPYS
		CALL	Permit

;--------------	now of course I could be write a quicksort algorithm, but
;--------------	really the following method is easier: I just try to find
;--------------	the maximum element, break it, then start the loop again
		move.l	a3,d7
		subi.l	#inputMemory+1,d7
.ra_Loop	move.l	d7,d2		; loop counter
		lea	(inputMemory),a3
		moveq	#0,d0		; current maximum
.ra_FindMax	move.b	(a3),d1
		cmp.b	d1,d0
		bge.b	.ra_NotMax
		move.b	d1,d0
		move.l	a3,a2
.ra_NotMax	addq.l	#1,a3
		dbra	d2,.ra_FindMax
		tst	d0
		beq.b	QuitSnoopyInstances_done
		clr.b	(a2)

;--------------	clear this element
		lea	(outputArgs),a1		
		move.l	d0,(a1)
		lea	(taskNameFormat,pc),a0
		lea	(currentTaskName),a2
		jsr	SPrintf
		lea	(currentTaskName),a1
		CALL	FindTask
		tst.l	d0
		beq.b	.ra_Loop
 		movea.l	d0,a1
		move.l	#SIGBREAKF_CTRL_C,d0
		CALL	Signal
		bra.b	.ra_Loop

.FINDSNOOPYS	movea.l	(LH_HEAD,a4),a4
.fs_LOOP	tst.l	(LN_SUCC,a4)
		beq.b	.fs_DONE
		bsr	.FINDSNOOPY
		tst.b	d0
		beq.b	.fs_SKIP
		move.b	d1,(a3)+
.fs_SKIP	movea.l	(LN_SUCC,a4),a4
		bra.b	.fs_LOOP
.fs_DONE	rts

		DONE	QuitSnoopyInstances
		
***********************************************************************************
;--------------	note: I use some very old code here stolen from a program I wrote
;--------------	some year ago together with a friend of mine (Stefan Scherer);
;--------------	this code isn't well documented but it works just fine so why bother....
		ENTRY	ReadWBArgs,d0-d7/a0-a6
 		lea	(TTHStruktur),a3
		move.l	(tth_WBStartup,a3),d0
		beq	ReadWBArgs_done
		tst.l	(argRdargs)
		bne	ReadWBArgs_done
		clr.l	(argRdargs)

;--------------	normal startup from workbench
.vonWorkbench	movea.l	d0,a3
		move.l	(sm_NumArgs,a3),d0
		movea.l	(sm_ArgList,a3),a3
		cmpi.l	#1,d0
		beq.b	.DEFSTART
		lea	(wa_SIZEOF,a3),a3

.DEFSTART	move.l	(wa_Lock,a3),d1			; DirLock zeigt auf
		CALL	CurrentDir,<(dosBase)>		; Pfad des Programms
		move.l	(wa_Name,a3),a0
.LeseIcon	CALL	GetDiskObject,<(iconBase)>	; ".info"-Datei lesen
		BRF	d0,ReadWBArgs_done		; Fehler, dann weiter

;--------------	Tool Types auswerten
		move.l	d0,a3
		cmpi.b	#WBPROJECT,(do_PAD_BYTE-1,a3)
		beq	.USEPROJECT
		move.l	(do_ToolTypes,a3),a3	
		move.l	a3,a0
		lea	(TTScript,pc),a1
		push	a0
		CALL	FindToolType
		pop	a0
		move.l	d0,(argScript)

.READTOOLTYPES	movea.l	a0,a5
		lea	(TTIncdir,pc),a1
		movea.l	a5,a0
		CALL	FindToolType
		move.l	d0,(argIncdir)
		lea	(TTOutput,pc),a1
		movea.l	a5,a0
		CALL	FindToolType
		move.l	d0,(argOutput)
		lea	(TTPri,pc),a1
		movea.l	a5,a0
		CALL	FindToolType
		tst.l	d0
		beq.b	.SKIPPRI
		movea.l	d0,a0
		moveq	#STVFORMAT_DEC,d0
		jsr	StringToValue
		move.l	d0,(priority)
.SKIPPRI	lea	(TTSticky,pc),a1
		lea	(argSticky),a2
		bsr.b	.GETBOOLEAN
		lea	(TTNoSegTracker,pc),a1
		lea	(argNoSegTracker),a2
		bsr.b	.GETBOOLEAN
		lea	(TTMatch,pc),a1
		lea	(argMatch),a2
		bsr.b	.GETBOOLEAN
		lea	(TTTaskInfo,pc),a1
		lea	(argTaskInfo),a2
		bsr.b	.GETBOOLEAN
		bra.b	ReadWBArgs_done

.GETBOOLEAN	movea.l	a5,a0
		push	a2
		CALL	FindToolType
		pop	a1
		tst.l	d0
		beq.b	.ISNTBOOL
		movea.l	d0,a0
		jsr	FindBooleanKeyword
.ISNTBOOL	rts

.USEPROJECT	push	a3
		lea	(TTHStruktur),a3
		move.l	(tth_WBStartup,a3),a3
		move.l	(sm_ArgList,a3),a3
		lea	(wa_SIZEOF,a3),a3
		move.l	(wa_Name,a3),(argScript)
		pop	a3
		move.l	(do_ToolTypes,a3),a3	
		move.l	a3,a0
		bra	.READTOOLTYPES
		DONE	ReadWBArgs

***********************************************************************************

		cstr	"$VER: ",IDSTRING

;--------------	library definitions
dosName		cstr	"dos.library"
intName		cstr	"intuition.library"
iconName	cstr	"icon.library"

;--------------	global text messages
defaultScript	cstr	DEFAULTSCRIPT
TIenabledMsg	cstr	'TaskInfo enabled\n'
TIdisabledMsg	cstr	'TaskInfo disabled\n'
taskinfoMsg	cstr	'Task %08lx : "%s"\n'
taskbcplMsg	cstr	'Task %08lx : "%b"\n'
helloMsg	cstr	"Welcome to [1m",IDSTRING,"[%ld][0m - written by ",AUTHORSTRING,"\n"
disableoutMsg	cstr	"[3mOutput disabled - press CTRL+E to enable again. [0m\n"
enableoutMsg	cstr	"[3mOutput enabled - press CTRL+D to disable. [0m\n"
windowName	cstr	"con:0/0/640/150/",IDSTRING

;--------------	template for dos/ReadArgs()
argTemplate	dc.b	"SCRIPT,"
		dc.b	"TASKINFO/S,"
		dc.b	"OUTPUT/K,"
		dc.b	"PRI/N,"
		dc.b	"SHOW/K/M,"
		dc.b	"STICKY/S,"
		dc.b	"MATCH/S,"
		dc.b	"QUIT/K/N,"
		dc.b	"INCDIR/K,"
		dc.b	"NOSEGTRACKER/S"
		dc.b	0

;--------------	workbench tooltypes
TTScript	cstr	"SCRIPT"
TTPri		cstr	"PRI"
TTOutput	cstr	"OUTPUT"
TTSticky	cstr	"STICKY"
TTNoSegTracker	cstr	"NOSEGTRACKER"
TTTaskInfo	cstr	"TASKINFO"
TTMatch		cstr	"MATCH"
TTIncdir	cstr	"INCDIR"

;--------------	name of SegTracker semaphore
SegTrackerName	cstr	"SegTracker"
SegTrackerMsg	cstr	"$%08lx - %s : Hunk %ld, Offset $%08lx, SegList $%08lx\n"
SegTrackerMsgTI	cstr	'PC=$%lx,N="%s",H=%ld,O=$%lx,S=$%lx,A=%lx,T="%s"\n'
SegTrackerFail	cstr	"(Segment not found)\n"
SegTrackerOn	dc.b	FALSE

;--------------	error messages for this module
nowindowErr	cstr	"ERROR, cannot open output stream"
nomsgportErr	cstr	"ERROR, cannot create msgport"
closesnoopyErr	dc.b	10,'*** WARNING *** Something else has patched the vectors Snoopy looks into'
		dc.b	10,'You will have to wait for the other program to finish and try Snoopy again'
		dc.b	10,'The function I have trouble with is the one with the template'
		dc.b	10,'"%s"',10,0
closeBeginIoErr	cstr	"ERROR, cannot remove DEV_BEGINIO() for %s\n",0
closeAbortIoErr	cstr	"ERROR, cannot remove DEV_ABORTIO() for %s\n",0
activeCallsErr	cstr	"ERROR, %ld calls still working",0
newlineMsg	cstr	'\n'

;--------------	new: flexible naming schedule
taskNameFormat	dc.b	"Snoopy Task."
taskNameFormat_SIZEOF	equ	*-taskNameFormat
		cstr	"%ld",0
taskPortFormat	cstr	"Snoopy Port.%ld",0

;--------------	success/failure messages
SuccessMsg	cstr	"YES"
FailureMsg	cstr	"NO"

;--------------	internal messages : not documented (try to find out yourself....)
abortIOMsg	cstr	"AbortIO"
beginIOMsg	cstr	"BeginIO"

;--------------	user messages
DebugDefTempMsg	dc.b	"**** Userfunction before call *********************************************",10
		dc.b	"Dx:%08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx",10
		dc.b	"Ax:%08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx",10
		dc.b	"():%08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx",10
		dc.b	"**** Userfunction after call",10
		dc.b	"Dx:%08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx",10
		dc.b	"Ax:%08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx",10
		dc.b	"():%08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx",10,0

		section	memory,bss
		
;--------------	library bases (guess what)
		defs.l	dosBase,1
		defs.l	intBase,1
		defs.l	iconBase,1

;--------------	all internal lists
		defs.b	hides,LH_SIZE
		defs.b	shows,LH_SIZE
		defs.b	bases,LH_SIZE
		defs.b	watches,LH_SIZE
		defs.b	deviceBases,LH_SIZE
		defs.b	aliases,LH_SIZE
		defs.b	defines,LH_SIZE
		defs.b	resources,LH_SIZE

;--------------	other global data
		defs.l	SegTracker,1	; SegTracker semaphore or NULL if it doesn't exist
		defs.l	myport,1	; port where all Snoopy messages arrive
		defs.l	window,1	; pointer to current output channel
		defs.l	thistask,1	; pointer to my own task
		defs.l	ActiveCalls,1	; counts the number of active calls

;--------------	internal data (locally used only within this module)
answersignal	ds.l	1	; the signalmask used for SendMsg()/GetMsg() answers
waitsignals	ds.l	1	; signals to Wait() for
message		ds.l	1	; last message recieved by GetMsg()
signals		ds.l	1	; signals actually recieved by Wait()
lastMessage	ds.l	1	; pointer to last message (for SKIPSIMILAR)

;--------------	stuff for ReadArgs
argRdargs	ds.l	1	; for allocated memory (dos.library INTERNALs)
argFirst	
		defs.l	argScript,1	; name of scriptfile or NULL for defaults
		defs.l	argTaskInfo,1	; TRUE if extended task info is to be given
		defs.l	argOutput,1	; name of redirected filename or NULL for the window
		defs.l	argPri,1	; priority number or NULL
		defs.l	argShow,1 	; array of tasknames to show or NULL
		defs.l	argSticky,1	; KEEP STICKY
		defs.l	argMatch,1	; use exact matching
		defs.l	argQuit,1	; remove snoopy instances
		defs.l	argIncdir,1
		defs.l	argNoSegTracker,1
outputactive	ds.b	1
		defs.b	showsactive,1

;--------------	contains the actual task/port names made by BuildSnoopysTaskname()
currentTaskName	ds.b	40
currentPortName	ds.b	40
currentInstance	ds.l	1	; instance counter

;--------------	segtracker output data
st_SegAddress	ds.l	1
st_SegName	ds.l	1
st_SegHunk	ds.l	1
st_SegOffset	ds.l	1
st_SegList	ds.l	1
st_SegTaskAddr	ds.l	1
st_SegFilename	ds.l	1
errorMsgBuffer	ds.b	256

***********************************************************************************
;--------------	
