;-----------------------------------------------------------------------------
;
; Program	: NoCare.asm V1.5
; Author 	: Raymond Hoving, The Orega Programming Group Holland
; Creation Date : Wed Jan 16 19:28:28 1991
; Purpose	: A utility to speed up the workbench window environment.
; Usage		: NoCare - Starts/stops the program from a CLI/Shell.                
;
; See copyright notes in file called 'NoCare.doc'.
; Bug reports or comments to:
;
;	The Orega Programming Group Holland,
;	P.o.box 499,				Apothekersdijk 24,
; 	2280 AL Rijswijk,			2312 DD Leiden,
;	The Netherlands.			The Netherlands.
;						Phone .. - (0)71 - 14 49 71
;
; or UUCP: breemen@rulcvx.LeidenUniv.nl (shared account).
;
;-----------------------------------------------------------------------------

OPENLIB	macro				; \1 library name.
	data				; \2 address of failure handling code.
\1name	dc.b	'\1.library',0		     Use 0 for \1fails.
	even	 			; \3 Optional version number.
\1base	ds.l	1			;    (Default value is 33)
	code				; Registers affected d0,a1,a6.
	lea.l	\1name,a1
	ifne	NARG-3
	move.l  #33,d0
	endc
	ifeq 	NARG-3
	move.l	#\3,d0
	endc
	CALL 	exec,OpenLibrary
	move.l	d0,\1base
	ifc 	'\2','0'
	beq	\1fails
	endc
	ifnc 	'\2','0'
	beq	\2
	endc
	endm

CLOSELIB macro				; \1 library name.
	move.l	\1base,a1		; Registers affected a1,a6.
	CALL	exec,CloseLibrary
	endm

CALL	macro				; \1 library name.
	ifnc	'\1','exec'		; \2 function name.
	move.l	\1base,a6		; Registers affected a6.
	endc
	ifc	'\1','exec'
	move.l	_SysBase,a6
	endc
	jsr	_LVO\2(a6)
	endm

CALLR	macro				; \1 function name.
	jsr	_LVO\1(a6)		; No registers affected.
	endm	

STANDARDOUTPUT macro			; Init of stdout, used by PRINT etc.
	data
stdout	ds.l	1
	code
	CALL dos,Output
	move.l	d0,stdout
	endm

PRINT	macro			 	; Prints string at location \1
	move.l	stdout,d1		; with lenght \2. No automatic
	move.l	#\1,d2			; linefeed.
	move.l	#\2,d3
	CALL dos,Write
	endm

_LVOOpenLibrary		equ	-552	; Library vector offsets.
_LVOCloseLibrary	equ	-414	; No need to link with amiga.lib.
_LVOOutput		equ	-60
_LVOWrite		equ	-48
_LVOAllocSignal		equ	-330
_LVOFreeSignal		equ	-336
_LVOFindTask		equ	-294
_LVOFindPort		equ	-390
_LVOAddPort		equ	-354
_LVORemPort		equ	-360
_LVOAllocEntry		equ	-222
_LVOWaitPort		equ	-384
_LVOGetMsg		equ	-372
_LVOPutMsg		equ	-366
_LVOReplyMsg		equ	-378
_LVOSetFunction		equ	-420
_LVOForbid		equ	-132
_LVOPermit		equ	-138
_LVOCopyMem		equ	-624
_LVOAddTail		equ	-246
_LVOAddTask		equ	-282
_LVORemTask		equ	-288
_LVOOpenWindow		equ	-204
_SysBase		equ	$4


	include exec/exec.i
	include intuition/intuition.i
	include	intuition/screens.i

REFRESHBIT0	equ	$06
REFRESHBIT1	equ	$07	; Bits in the nw_Flags field.
		
STACKSIZE	equ	200	; Stack size for the NoCare task.

MN_CONTENTS	equ	MN_SIZE
REPLY		equ	$0
FAILURE		equ	$1
SUCCESS		equ	$2
REMOVE		equ	$3	; Inter-task communication defenitions.

	code

; Open dos.library and print message.

	OPENLIB dos,0
	STANDARDOUTPUT
	PRINT	mess1,mess2-mess1

; Create a message port for communication with the NoCare task:

	move.l	#-1,d0
	CALL	exec,AllocSignal
	move.b	d0,signal1
	bmi	signal1fails		; Allocate a signal.
	sub.l	a1,a1
	CALLR	FindTask
	lea.l	port1,a1
	move.l	d0,MP_SIGTASK(a1)	; Link this task into messageport.
	move.b	signal1,MP_SIGBIT(a1)	; Link allocated signal into port.
	CALLR	AddPort			; Make the port a public one.

; Is NoCare active already?

	CALLR	Forbid
	lea.l	port2name,a1		; Try to find NoCare2.Orega port.
	CALLR	FindPort
	move.l	d0,d6
	CALLR	Permit
	tst.l	d6
	bne	sendremovemessage

; The NoCare2.Orega port has not been found, so start the NoCare task:
; First allocate memory for the task structure, the stack and a safe
; place for the PC-relative code of the NoCare task.

	lea.l	memlist1,a0
	CALLR	AllocEntry
	bclr.l	#31,d0
	bne	allocerror		; Not enough memory!
	move.l	d0,a5			; Remember address in a5.

; Then copy the PC-relative taskcode to the safe place:

	move.l	LN_SIZE+10(a5),a1	; Get pointer to allocated memory.
	lea.l	codestart,a0
	move.l	#codeend-codestart,d0	
	CALLR	CopyMem
	
; Then create the new task (has no name yet):

	move.l	LN_SIZE+2(a5),a0	; Get pointer to allocated memory.

; Link stack, task structure and code area into task's memlist:

	lea.l	TC_MEMENTRY(a0),a0	; Get pointer to task's memlist.
	move.b	#NT_MEMORY,LN_TYPE(a0)
	move.l  a0,(a0)
	addq.l  #LH_TAIL,(a0)
	clr.l   LH_TAIL(a0)		; Initialise list structure.
	move.l  a0,(LH_TAIL+LN_PRED)(a0)
	
	move.l	a5,a1
	CALLR	AddTail			; Add node to memlist.

	move.l	LN_SIZE+2(a5),d0
	move.l	d0,a1
	add.l	#TC_SIZE,d0
	move.l	d0,TC_SPLOWER(a1)	; Lower stack boundary,
	add.l	#STACKSIZE,d0
	move.l	d0,TC_SPUPPER(a1)	; Upper stack boundary.
	move.l	d0,TC_SPREG(a1)		; Initial stack pointer.
	move.b	#NT_TASK,LN_TYPE(a1)	; Node type.
	move.l	LN_SIZE+10(a5),a2	; Initial PC of NoCare task.
	sub.l	a3,a3
	CALLR	AddTask			; Lets's go!

RemTask

; The NoCare task has been launched, now wait for the startup message:

getmsgloop1
	lea.l	port1,a0
	CALLR	WaitPort		; Wait for a message.
	lea.l	port1,a0
	CALLR	GetMsg			; Get the new message.
	move.l	d0,a1
	move.w	MN_CONTENTS(a1),d6	; Save the important field.
	move.w	#REPLY,MN_CONTENTS(a1)	; Mark as a replied message.
	CALLR	ReplyMsg		; Quickly reply message.

; Examine the startup message now:

	cmp.w	#FAILURE,d6
	beq.s	installfailed
	cmp.w	#SUCCESS,d6
	bne.s	getmsgloop1		; Should never execute this.
	PRINT	mess2,mess3-mess2	; 'Installed.'
	bra	ex1
installfailed
	PRINT	mess4,mess5-mess4	; 'Could not install.'
	bra	ex1

; Send a message to the messageport of the NoCare task.
; This informs the NoCare task to remove the OpenWindow patch.
; The address of the messageport is in d6:

sendremovemessage
	lea.l	message1,a1
	move.w	#REMOVE,MN_CONTENTS(a1)
	move.l	d6,a0			; Destination port.
	CALLR	PutMsg
getmsgloop2
	lea.l	port1,a0
	CALLR	WaitPort		; Wait for reply.
	lea.l	port1,a0
	CALLR	GetMsg
	move.l	d0,a0
	move.w	MN_CONTENTS(a0),d0

; We asked the NoCare task to remove itself (and of course the
; OpenWindow patch). Examine the reply of the NoCare task:

	cmp.w	#FAILURE,d0
	beq.s	remerror
	cmp.w	#SUCCESS,d0
	bne.s	getmsgloop2		; Should never execute this.
	PRINT	mess3,mess4-mess3	; 'Removed.'
	bra.s	ex1
remerror
	PRINT	mess5,mess6-mess5	; 'Could not remove.'
	bra.s	ex1

; Error handling code:

signal1fails
	PRINT	mess6,mess7-mess6
	bra.s	ex0
allocerror
	PRINT	mess6,mess7-mess6

; Cleanup code follows:

ex1	lea.l	port1,a1
	CALL	exec,RemPort
	clr.l	d0
	move.b	signal1,d0		; Free signal.
	CALLR	FreeSignal
ex0	CLOSELIB dos
dosfails
	rts

	data

message1	dc.l	0,0		; ln_succ, ln_pred
		dc.b	NT_MESSAGE,0	; ln_type, ln_pri
		dc.l	0		; ln_name
		dc.l	port1		; mn_replyport
		dc.w	MN_SIZE+2	; mn_length
		dc.w	0		; mn_contents

port1		dc.l	0,0		; ln_succ, ln_pred
		dc.b	NT_MSGPORT,1	; ln_type, ln_pri
		dc.l	port1name	; ln_name
		dc.b	PA_SIGNAL,0	; mp_flags, mp_sigbit
		dc.l	0		; mp_sigtask
		dc.l	0,0,0		; lh_head, lh_tail, lh_tailpred
		dc.b	NT_MESSAGE,0	; lh_type, lh_pad

memlist1	dc.l	0,0		; ln_succ, ln_pred
		dc.b	NT_MEMORY,0	; ln_type, ln_pri
		dc.l	0		; ln_name
		dc.w	2		; Numer of entries.
		dc.l	MEMF_PUBLIC!MEMF_CLEAR
		dc.l	STACKSIZE+TC_SIZE
		dc.l	MEMF_PUBLIC
		dc.l	codeend-codestart

signal1		ds.l	1
port1name	dc.b	'NoCare1.Orega',0
port2name	dc.b	'NoCare2.Orega',0
mess1		dc.b	$1b,'[33mNoCare V1.5',$1b,'[0m by Raymond Hoving,'
		dc.b	' THE OREGA PROGRAMMING GROUP HOLLAND.',$a
mess2		dc.b	'OpenWindow() patch installed.',$a
mess3		dc.b	'OpenWindow() patch removed.',$a
mess4		dc.b	'ERROR: Could not install.',$a
mess5		dc.b	'ERROR: Could not remove.',$a
mess6		dc.b	'ERROR: Out of memory/signals',$a
mess7

;-----------------------------------------------------------------------------
; Now follows the code of the NoCare task. This code is PC-relative.
; First we give this task a name:

	code

codestart
	sub.l	a1,a1
	CALL	exec,FindTask
	move.l	d0,a0
	lea.l	taskname(PC),a1
	move.l	a1,LN_NAME(a0)

; Open intuition.library:

	lea.l	intuitionname(PC),a1
	clr.l	d0
	CALLR	OpenLibrary
	lea.l	intuitionbase(PC),a2
	move.l	d0,(a2)
;	beq.s	intuitionfails		; Any version will do.

; Then patch the OpenWindow() function:

	move.l	d0,a1
	move.w	#_LVOOpenWindow,a0
	lea.l	newopenwindow(PC),a2
	move.l	a2,d0
	CALLR	SetFunction
	lea.l	oldopenwindow(PC),a2
	move.l	d0,(a2)

; Create a message port (called NoCare2.Orega)

	move.l	#-1,d0
	CALLR	AllocSignal
;	bmi	signal2fails		; It's my task!
	lea.l	signal2(PC),a2
	move.b	d0,(a2)
	sub.l	a1,a1
	CALLR	FindTask
	lea.l	port2(PC),a1
	move.l	d0,MP_SIGTASK(a1)
	lea.l	port2name2(PC),a0
	move.l	a0,LN_NAME(a1)
	move.b	signal2(PC),MP_SIGBIT(a1)
	CALLR	AddPort			; Add port to system.

; Now send a message to our parent task, that NoCare is installed
; without problems:
	
	CALLR	Forbid
	lea.l	port1name2(PC),a1	; Try to find NoCare1.Orega port.
	CALLR	FindPort
	move.l	d0,d6			; Remember port in d6.
	CALLR	Permit
	tst.l	d6
;	beq	port1gone		; Impossible situation!

	lea.l	port2(PC),a0
	lea.l	message2(PC),a1
	move.l	a0,MN_REPLYPORT(a1)
	move.w	#SUCCESS,MN_CONTENTS(a1)
	move.l	d6,a0			; Destination port.
	CALLR	PutMsg

; Then wait for the REPLY message or the REMOVE message:

getmsgloop3
	lea.l	port2(PC),a0
	CALLR	WaitPort		; Wait for message.
	lea.l	port2(PC),a0
	CALLR	GetMsg
	move.l	d0,a0
	cmp.w	#REMOVE,MN_CONTENTS(a0)
	bne.s	getmsgloop3		; It's the REPLY message.
	move.l	d0,a5			; Remember this message.

; So the user wants to terminate NoCare.
; First test if someone else patched our patch.

	CALLR	Forbid
	move.l	intuitionbase(PC),a3
	move.l	_LVOOpenWindow+2(a3),a3	; Get OpenWindow vector.
	lea.l	newopenwindow(PC),a4
	cmp.l	a3,a4
	bne.s	removeerror

; Now we restore the old OpenWindow vector:

	move.l	oldopenwindow(PC),d0	; Pointer to original code.
	move.l	intuitionbase(PC),a1
	move.w	#_LVOOpenWindow,a0
	CALLR	SetFunction
	CALLR	Permit

; Then we remove the messageport and free the signal bit:

	lea.l	port2(PC),a1
	CALLR	RemPort
	clr.l	d0
	move.b	signal2(PC),d0
	CALLR	FreeSignal		; Just to be nice.

; Reply the REMOVE message:

	move.l	a5,a1			; Pointer to the REMOVE message.
	move.w	#SUCCESS,MN_CONTENTS(a1)
	CALLR	ReplyMsg

; Close intuition.library:

	move.l	intuitionbase(PC),a1
	CALLR	CloseLibrary

; Then call exec's standard task finalizer. The task structure,
; it's stack and the safeplace-area will be freed.

	sub.l	a1,a1
	CALLR	RemTask			; End of NoCare task.

;-----------------------------------------------------------------------------
; When NoCare could not be removed, we reply with a failure type message.

removeerror
	CALLR	Permit
	move.l	a5,a1
	move.w	#FAILURE,MN_CONTENTS(a1)
	CALLR	ReplyMsg
	bra	getmsgloop3		; Wait for another try...

;-----------------------------------------------------------------------------
; As OpenWindow() is called, then a0 points to a NewWindow structure.
; We patch a bit in the nw_Flags field and than jump to the original
; OpenWindow() code.

newopenwindow
	cmp.w	#WBENCHSCREEN,nw_Type(a0)
	bne.s	dontchange
	move.l	nw_Flags(a0),d0
	bclr	#REFRESHBIT0,d0		; We don't want SIMPLE_REFRESH ones.
	move.l	d0,nw_Flags(a0)
dontchange
	move.l	oldopenwindow(PC),a1
	jmp	(a1)			; Jump to original code.

;-----------------------------------------------------------------------------

message2	dc.l	0,0		; ln_succ, ln_pred
		dc.b	NT_MESSAGE,0	; ln_type, ln_pri
		dc.l	0		; ln_name
		dc.l	0		; mn_replyport
		dc.w	MN_SIZE+2	; mn_length
		dc.w	0		; mn_contents

port2		dc.l	0,0		; ln_succ, ln_pred
		dc.b	NT_MSGPORT,1	; ln_type, ln_pri
		dc.l	0		; ln_name
		dc.b	PA_SIGNAL,0	; mp_flags, mp_sigbit
		dc.l	0		; mp_sigtask
		dc.l	0,0,0		; lh_head, lh_tail, lh_tailpred
		dc.b	NT_MESSAGE,0	; lh_type, lh_pad

oldopenwindow	ds.l	1
signal2		ds.l	1
intuitionbase	ds.l	1
taskname	dc.b	'NoCareTask.Orega',0
port1name2	dc.b	'NoCare1.Orega',0
port2name2	dc.b	'NoCare2.Orega',0
intuitionname	dc.b	'intuition.library',0

codeend

	end

;-----------------------------------------------------------------------------
