;$VER: MyMouse.asm 1.2 (12.02.93)
;Written by Andrew Forrest
;Based on QMouse by Dan Babcock

;Assembled with Devpac 3
	nolist

; How it works:
;  The program spawns a child process which remains resident in memory and
;  does the funky stuff. If the resident process already exists, the program
;  alters the child's parameters and sends it an update signal. The main
;  program allocates stuff that's _always_ needed, the child allocates other
;  stuff on the fly (input.device, for example).

;  While it is setting up, the child makes itself invisible by changing its
;  name. This ensures that a seach for the child (by name) will always find
;  the child prepared.

; PROCESS IS GUARUNTEED TO STAY PUT (cos you can't totally unpatch functions)

	include	everything.i
	include	hardware/dmabits.i
	
Execbase	equ	4
_custom	equ	$dff000

	;*»»  Semaphore macros
	; Use carefully!
	; Take a5=GlobalPtr; a6=ExecBase
	; Scratch a0
CriticalSection	macro
	  lea	  MySemaphore(a5),a0
	  just	  ObtainSemaphore
	endm
EndCritical	macro
	  lea	  MySemaphore(a5),a0
	  just	  ReleaseSemaphore
	endm

BlankScreen	macro
	  move.w	  #DMAF_COPPER+DMAF_RASTER,dmacon+_custom.l
	  clr.w	  color+_custom.l
	endm
UnBkScreen	macro
	  move.w	  #DMAF_SETCLR+DMAF_COPPER+DMAF_RASTER,dmacon+_custom.l
	endm

	;*»»  Code entrypoint
	move.l	Execbase,a6
	cmp.w	#37,LIB_VERSION(a6)
	bhs	GoodVersion
	
;Oh no, we're running under 1.3 or something
	lea	DosName(pc),a1
	moveq.l	#0,d0
	just	OpenLibrary
	tst.l	d0
	  beq.s	  ErrorEnd
	move.l	d0,a6
	just	Output
	move.l	d0,d1
	moveq	#SorryLength,d3
	lea	Sorry(pc),a0
	move.l	a0,d2
	just	Write
	move.l	a6,a1
	move.l	Execbase,a6
	just	CloseLibrary
ErrorEnd:	moveq	#RETURN_FAIL,d0
	rts

	dc.b	'$VER: MyMouse 1.2 (08.02.94)',LF,0

Sorry	dc.b	'You need (at least) Kickstart 2.0 to run this program.',LF,0
SorryLength	equ	*-Sorry
	even

GoodVersion:
***************************************************************************
* Start of Main program
***************************************************************************
; Registers:
;  A5=^Global data structure
;  D7=^Installed process, _if it exists yet_, else NULL

	lea	ProcName(pc),a1
	just	FindTask
	move.l	d0,d7
	bne.s	.else	; If process doesn't exist then
	  bsr	  Allocate	;   Reserve some space for it
	   bne	   Deallocate
	  bsr.s	  Read_Args	; (returns Z flag success)
	   bne	   Deallocate
	  bsr	  Install	; (returns pointer to new task)
	   beq	   Deallocate
	bra.s	.end_ok	; else
.else	  move.l	  d7,a1
	  move.l	  TC_Userdata(a1),a5	; Get global structure
	  bsr.s	  Read_Args
	   bne.s	   .end_fail	;(If couldn't read args, fail)
	  move.l	  d7,a1
	  move.l	  UpdateSig(a5),d0
	  just	  Signal	;   Send task a signal to wake it up
.end_ok	moveq	  #RETURN_OK,d0
.end_fail	rts

***************************************************************************
Read_Args:	;*»»  Replace arguments in global structure with new ones
; Takes a5=GlobalPtr; Returns OK or FAIL code; a6=Execbase
; Scratches d0-d4/a0-a1/a4
	CriticalSection
	lea	Template(pc),a0
	move.l	a0,d1
	lea	Options(a5),a0
	move.l	a0,d2
	moveq	#NumOptions-1,d0
.clear_loop	  clr.l	  (a0)+
	dbra	d0,.clear_loop
	moveq	#0,d3	;no custom rdarg structure
	move.l	DosBase(a5),a6
	just	ReadArgs
	tst.l	d0
	  beq.s	  .fail
	move.l	ArgPtr(a5),d1	; get _old_ arguments
	beq.s	.use
	  push.l	  d0
	  just	  FreeArgs	; and free them if necessary
	  pop.l	  d0
.use	move.l	d0,ArgPtr(a5)	; then install _new_ arguments
	move.l	Execbase,a6
	moveq.l	#RETURN_OK,d0

.end	EndCritical
	tst.l	d0		; (return D0 and CCR)
	rts
	 
.fail	move.l	Execbase,a6
	moveq.l	#RETURN_FAIL,d0
	bra.s	.end

***************************************************************************
Install:
; Takes a5=GlobalPtr; a6=Execbase; Returns d0=^NewTask or NULL
; Scratches a0-a1/d0-d1
	;*»»  Copy resident part of code into allocated memory
	lea	StartResidentCode(pc),a0	;source
	lea	ResidentCode(a5),a1	;destination
	move.l	#ResidentCodeLength,d0	;size
	just	CopyMem
	just	CacheClearU	;for 020/030/040

	;*»»  Initialize static data
	move.b	#HANDLERPRI,InputInterrupt+LN_PRI(a5)
	move.l	a5,InputInterrupt+IS_DATA(a5)
	lea	IntRoutine-StartResidentCode+ResidentCode(a5),a0
	move.l	a0,InputInterrupt+IS_CODE(a5)

	move.l	a5,GlobalPtr-StartResidentCode+ResidentCode(a5)

	;*»»  Get stack for command.
	move.l	DosBase(a5),a6
	just	Cli
	move.l	d0,a0
	move.l	cli_DefaultStack(a0),d0
	lsl.l	#2,d0
	move.l	d0,CmdStackSize-StartResidentCode+ResidentCode(a5)

	;*»»  Start the baby process
	lea	NewProcTags-StartResidentCode+ResidentCode(a5),a0
	lea	NEW_LIFE-StartResidentCode+ResidentCode(a5),a1
	move.l	a1,EntryPointTag(a0)
	lea	ProcName-StartResidentCode+ResidentCode(a5),a1
	not.b	(a1)	; (So task is initially invisible)
	move.l	a1,ProcNameTag(a0)
	move.l	a0,d1	; Get tag list into d1
	just	CreateNewProc
	tst.l	d0

	move.l	Execbase,a6
	rts

***************************************************************************
Allocate:
; Takes a6=Execbase; Returns OK or FAIL; a5=GlobalPtr or NULL
; Scratches a0-a3/d0-d1
	;*»»  Allocate global data structure
	move.l	#Data_SIZEOF,d0
	move.l	#MEMF_CLEAR|MEMF_PUBLIC,d1	;(Got to be PUBLIC)
	just	AllocMem
	move.l	d0,a5	; Need to store in a5 _before_ testing
	tst.l	d0
	  beq.s	  .cant_alloc

	;*»»  Allocate Chip RAM data
	move.l	#MouseData_SIZEOF,d0
	move.l	#MEMF_CLEAR|MEMF_PUBLIC|MEMF_CHIP,d1
	just	AllocMem
	move.l	d0,MouseChipData(a5)
	  beq.s	  .cant_alloc

	;*»»  Open libraries
	lea	LibTable(pc),a2
	lea	FirstLibBase(a5),a3
	moveq	#NumLibs-1,d2
.loop	  move.w	  (a2)+,d0	; library version to open
	  ext.l	  d0	; version should be longword
	  move.w	  (a2)+,a1
	  lea	  LibTable(pc,a1.w),a1	; name of library
	  just	  OpenLibrary
	  move.l	  d0,(a3)+
	  beq.s	  .cant_alloc
	dbra	d2,.loop

	;*»»  Set up semaphore
	lea	MySemaphore(a5),a0
	just	InitSemaphore

	moveq	#RETURN_OK,d0	; Set d0 and CCR
	rts
	
.cant_alloc	moveq	#RETURN_FAIL,d0
	rts

***************************************************************************
LibTable:	dc.w	37,DosName-LibTable
	dc.w	33,IntName-LibTable
	dc.w	33,LayName-LibTable
NumLibs:	equ	(*-LibTable)/4

***************************************************************************
***************************************************************************
StartResidentCode:	;The following code is copied into allocated memory.
***************************************************************************
; Everything except Deallocate is called only from child process
***************************************************************************
Deallocate:	;*»» Close libraries (called either from parent or child)
; Takes a5=GlobalPtr; a6=Execbase; No return value.
; Scratches a0-a2/d0-d2
	move.l	a5,d0	; Does global structure exist?
	  beq.s	  .noData

	lea	FirstLibBase(a5),a2
	moveq	#NumLibs-1,d2
.loop	move.l	(a2)+,d0
	beq.s	.endif
	  move.l	  d0,a1
	  just	  CloseLibrary
.endif	dbra	d2,.loop

	;*»» Free chip data.
	move.l	MouseChipData(a5),d0
	beq.s	.noChipData
	  move.l	  d0,a1
	  move.l	  #MouseData_SIZEOF,d0
	  just	  FreeMem
.noChipData
	;*»» Free global structure
	lea	(a5),a1
	move.l	#Data_SIZEOF,d0
	jmp	_LVOFreeMem(a6)	; Jump in case we are
.noData				; deallocating _this_ code
	rts

***************************************************************************
; Here lies the start of the child process' program
; Except where noted, subroutines should preserve a5-a6 (and the stack
; pointer of course), but may trash other registers
NEW_LIFE:	move.l	Execbase,a6
	move.l	GlobalPtr(pc),a5
	bsr	InitialiseResident	; Get signals
	  bne.s	  Deallocate	; (_not_ a branch to subrtn)

BIG_LOOP:	bsr	EvaluateOptions
	  bne.s	  KillHandler
	bsr	AllocateResident	; Get so far ungot resources
wait_loop:	move.l	AllSignals(a5),d0
	just	Wait	; \ Routines called from this section
	move.l	d0,d7	; / may trash any register except a5 or d7
	and.l	CLISig(a5),d0
	beq.s	.noCLI
*	  move.l	  Execbase,a6	(already the case)
	  bsr	  CLI	; Expects a6=Execbase (nobody's perfect:-)
.noCLI	move.l	d7,d0
	and.l	MouseBlankSig(a5),d0
	beq.s	.noMB
	  bsr	  MouseBlankUpdate
.noMB	tst.l	ActivateScreenOption(a5)
	beq.s	.noActiWind	; If window-activation is off anyway then
	  move.l	  d7,d0	; ...don't bother to check for the signals
	  and.l	  CloseScreenSig(a5),d0
	  beq.s	  .noClose
	    bsr	    Closure
.noClose	  move.l	  d7,d0
	  and.l	  ScreenDepthSig(a5),d0
	  beq.s	  .noDepth
	    bsr.s	    Depth
.noDepth
.noActiWind	move.l	Execbase,a6
	and.l	UpdateSig(a5),d7
	  beq.s	  wait_loop
	bra.s	BIG_LOOP

KillHandler:
	bclr	#STB_HandlerInstalled,Status(a5)
	  beq.s	  .end
	move.l	InputIORequest(a5),a1
	move.w	#IND_REMHANDLER,IO_COMMAND(a1)
	lea	InputInterrupt(a5),a0
	move.l	a0,IO_DATA(a1)
	just	DoIO
	bclr	#STB_DoMBlank,Status(a5)
	bsr	MouseBlankUpdate	; Switch off mouse-blanking
	bclr	#STB_SBlanked,Status(a5)
	UnBkScreen			; Switch off screen-blanking
.end	bra.s	wait_loop

;****************************************************************************
; This routine is called asynchronously whenever Intuition's ScreenDepth
; routine gets called (i.e. any time a screen is rearranged).
Depth:	move.l	IntBase(a5),a6
	moveq	#0,d0
	just	LockIBase	; Start critical section
	move.l	d0,d2
	move.l	ib_ActiveScreen(a6),a0	; \  Remember to keep track
	move.l	ib_ActiveWindow(a6),a1	;  | of _previous_ screen-
	bsr	AssociateWindow	; /  window combination
	move.l	ib_FirstScreen(a6),a0	; \ Look up `active' window
	bsr	IndexScreenTable	; / of newly frontmost screen
	tst.l	d0
	beq.s	.fail	; If we can find a window for screen,
	  move.l	  d0,a0	; ...then activate that window
	  just	  ActivateWindow ; (Defered so should be OK inside lock)
.fail	move.l	d2,a0
	just	UnlockIBase	; End critical section
	rts

; This one gets called on screen closure.
Closure:	move.l	IntBase(a5),a6
	moveq	#0,d0
	just	LockIBase	; Start critical section
	move.l	d0,a0
	bsr	PruneScreenTable	; (Alters no registers)
	just	UnlockIBase	; End critical section
	or.l	ScreenDepthSig(a5),d7	; (Must change window too)
	rts

; Executes a CLI command (like NewShell, for example)
; *** Expects a6=Execbase
CLI:	CriticalSection	; Critical so CMD parameter can't change
	move.l	CmdOption(a5),d1	;Pop CLI
	beq.s	.fail
	  pushm.l	  d1/a6
	  move.l	  IntBase(a5),a6	;Workbench To Front
	  just	  WBenchToFront
	  popm.l	  d1/a6
	  move.l	  DosBase(a5),a6
	  lea	  Tags(pc),a1
	  bsr.s	  OpenNil
	  move.l	  d0,InputTag(a1)
	beq.s	.end
	  bsr.s	  OpenNil
	  move.l	  d0,OutputTag(a1)
	beq.s	.closeinput
	  move.l	  a1,d2	;tags
	  just	  SystemTagList
	  tst.l	  d0
	  beq.s	  .end	; If sucessful, should deallocate itself
	    move.l	    OutputTag+Tags(pc),d1
	    just	    Close
.closeinput	    move.l	    InputTag+Tags(pc),d1
	    just	    Close
.end	  move.l	  Execbase,a6
.fail	EndCritical
	rts	

Tags:
	dc.l	SYS_UserShell,0
	dc.l	SYS_Asynch,0
	dc.l	SYS_Input,0
InputTag	equ	*-4-Tags
	dc.l	SYS_Output,0
OutputTag	equ	*-4-Tags
	dc.l	NP_Priority,ShellPriority
	dc.l	NP_StackSize
CmdStackSize:
	dc.l	0
	dc.l	0	;end of tag list

OpenNil:	pushm.l	a0-a1/d1-d2
	lea	NilName(pc),a0
	move.l	a0,d1
	move.l	#MODE_READWRITE,d2
	just	Open
	popm.l	a0-a1/d1-d2
	rts

;****************************************************************************
InitialiseResident:
; Takes a5=GlobalPtr; a6=Execbase; Returns OK or FAIL
; Scratches d0-d2/a0-a1
	suba.l	a1,a1
	just	FindTask	; _Find_ yourself.
	move.l	d0,a0
	move.l	a5,TC_Userdata(a0)
	move.l	d0,Task(a5)
	bsr	InitialiseScreenTable	; Just set it up

	;*»»  Allocate signals
	moveq	#NumSigs-1,d2
	lea	Sigs(a5),a2
.sigloop	  moveq	  #-1,d0
	  just	  AllocSignal
	  tst.l	  d0
	bmi.s	.fail
.ok	  moveq	  #0,d1
	  bset	  d0,d1
	  move.l	  d1,(a2)+
	  or.l	  d1,AllSignals(a5)
	dbra	d2,.sigloop

	just	Forbid
	lea	ProcName(pc),a1
	move.b	#InitialLetter,(a1)
	just	FindTask	; Search for process with same name
	tst.l	d0
	  bne.s	  .found_twin	; Oh, no -- duplicate name
	move.l	Task(a5),a1
	move.l	LN_NAME(a1),a1
	move.b	#InitialLetter,(a1)	; make process visible
	just	Permit

	;*»»  Patch three Intuition routines. (Do this last)
	;*»»  (all but first patch must be on V39 library)
	lea	PatchTable(pc),a2
	lea	OldRoutines(a5),a3
	move.l	IntBase(a5),a1	; Library to patch
.patchloop	  move.w	  (a2)+,d1	; LVO offset
	beq.s	.endpatch	; (exit if hit null-terminator)
	  move.w	  (a2)+,d0	; Offset to new routine
	  tst.l	  (a3)	; If it is already patched
	  bne.s	  .nextpatch	;   skip to the next one
	    lea	    PatchTable(pc,d0.w),a0 ; Get address of new routine
	    move.l	    a0,d0	; d0 <- address of new routine
	    move.w	    d1,a0	; a0 <- LVO offset
	    just	    SetFunction
	    move.l	    d0,(a3)+	; Store ^old routine for posterity
.nextpatch	  move.l	  IntBase(a5),a1	;\ Get ^Intuition for next
	  cmp.w	  #39,LIB_VERSION(a1)	;/ iteration and check version
	bhs.s	.patchloop	; Patch V39 Intuition only
.endpatch	

	moveq	#RETURN_OK,d0
	rts
	
.found_twin	
.fail	moveq	#31,d2
.loop	  btst	  d2,AllSignals(a5)
	  beq.s	  .next
	    move.l	    d2,d0
	    just	    FreeSignal
.next	dbra	d2,.loop
	moveq	#RETURN_FAIL,d0
	rts

PatchTable:	dc.w	_LVODisplayBeep,NewDisplayBeep-PatchTable
	dc.w	_LVOCloseScreen,NewCloseScreen-PatchTable
	dc.w	_LVOScreenDepth,NewScreenDepth-PatchTable
	dc.w	0	; NULL-terminate list

***************************************************************************
NewDisplayBeep:
	move.l	GlobalPtr(pc),a1
	tst.l	NoBeepOption(a1)
	bne.s	.endif
	  move.l	  OldDisplayBeep(a1),-(sp)
.endif	rts

NewCloseScreen:
	pushm.l	a2/a5-a6
	move.l	GlobalPtr(pc),a5
	move.l	OldCloseScreen(a5),a2
	jsr	(a2)
	move.l	d0,a2	; (Preserve return value)
	move.l	CloseScreenSig(a5),d0
	bra.s	sig

NewScreenDepth:
	pushm.l	a2/a5-a6
	move.l	GlobalPtr(pc),a5
	move.l	OldScreenDepth(a5),a2
	jsr	(a2)
	move.l	ScreenDepthSig(a5),d0

sig	move.l	Task(a5),a1
	move.l	Execbase,a6
	just	Signal
	move.l	a2,d0	; (Restore return value)
	popm.l	a2/a5-a6
	rts


***************************************************************************
; Here is the routine for allocating the baby task's resources
; If they cannot be allocated at first, the program tries again every time
; an update signal is recieved.
AllocateResident:
; Takes a5=GlobalPtr; a6=Execbase
; Scratches a0-a3/d0-d1
	;*»»  Create an IORequest structure
	move.l	InputMsgPort(a5),d0
	  bne.s	  .endCreatePort	; Skip if port already there
	just	CreateMsgPort
	move.l	d0,InputMsgPort(a5)
	beq.s	.fail
.endCreatePort
	move.l	d0,a0
	move.l	InputIORequest(a5),d0	; n.b. _not_ "tst.l Inpu..."
	  bne.s	  .endCreateReq	; Skip if req already there
	moveq	#IOSTD_SIZE,d0
	just	CreateIORequest
	move.l	d0,InputIORequest(a5)
	beq.s	.fail
.endCreateReq

	;*»»  Open the input.device
	btst	#STB_InputDeviceOpened,Status(a5)
	  bne.s	  .endOpenInput
	move.l	d0,a1	;IO request
	lea	InputName(pc),a0	;device name
	moveq	#0,d0	;unit number
	moveq	#0,d1	;flags
	just	OpenDevice
	tst.l	d0
	bne.s	.fail
	  bset	  #STB_InputDeviceOpened,Status(a5)
.endOpenInput

	;*»»  Install the input handler
	bset	#STB_HandlerInstalled,Status(a5)
	  bne.s	  .endInstallHandl
	move.l	InputIORequest(a5),a1
	move.w	#IND_ADDHANDLER,IO_COMMAND(a1)
	lea	InputInterrupt(a5),a0
	move.l	a0,IO_DATA(a1)
	just	DoIO
.endInstallHandl

.fail	rts

***************************************************************************
EvaluateOptions:
; Takes a5=GlobalPtr; a6=Execbase; Scratches d0-d1/a0-a2
	;*»»  Copy some parameters (for input-handler)
	CriticalSection	; So no one changes them while we do it
	lea	CacheTable(pc),a1
	lea	OptionCaches(a5),a2
	move.w	#CacheNum-1,d1
.loop	  move.w	  (a1)+,d0
	  move.l	  (a5,d0.w),d0
.if	  beq.s	  .else
	    move.l	    d0,a0
	    move.l	    (a0),d0
	  bra.s	  .endif
.else	    moveq	    #-1,d0	; Unselected options become -1
.endif	  move.l	  d0,(a2)+
	dbra	d1,.loop
.endloop	EndCritical

	;*»»  Ensure mouse/screen is never left blanked
	tst.l	MouseBlank(a5)
	bge.s	.mblnk_on	; Skip if mouse blanking was turned on
	  bclr	  #STB_DoMBlank,Status(a5)
	  bsr	  MouseBlankUpdate 
.mblnk_on	tst.l	ScreenBlank(a5)
	bgt.s	.sblnk_on	; Skip if screen blanking was turned on
	  bclr	  #STB_SBlanked,Status(a5)
	  UnBkScreen
.sblnk_on

	;*»»  Initialize damping constant.
	move.l	Acceleration(a5),d0
	  bmi.s	  .no_damp
	move.l	d0,a0
	move.l	(a0),d0
	subq.l	#1,d0
	move.l	Threshold(a5),d1
	bpl.s	.use_thresh
	  moveq	  #0,d1
.use_thresh	addq.l	#1,d1
	mulu.w	d0,d1
	move.w	d1,DampingConstant(a5)
.no_damp
.okay
	*»»  Check that at least one command-line option was specified
	lea	Options(a5),a0
	moveq	#NumOptions-1,d0
.chk_loop	  tst.l	  (a0)+
	  bne.s	  .dontdie
	dbra	d0,.chk_loop
	bsr	ShutDownScreenTable	; If no options deallocate mem
	moveq	#RETURN_FAIL,d0	; Return nonzero if no options
	rts
.dontdie	moveq	#RETURN_OK,d0	; Return zero if at least 1 selected
	rts

CacheTable	dc.w	MBlankOption
	dc.w	SBlankOption
	dc.w	AccelOption
	dc.w	ThreshOption
	dc.w	CTFOption
	dc.w	ClickBorderOption
CacheNum	equ	(*-CacheTable)/2

;****************************************************************************
	include	MM-Handler.asm
	include	ScreenTable.asm

;****************************************************************************
;Utility routines

	;*»»  Get window associated with current mouse position.
; Takes a5=GlobalPtr; a6=IntBase; Returns d0=Window; D1=Screen
	; (assume we don't need to lock IntuitionBase)
GetWindow:
	pushm.l	d4/a0-a1/a4/a6
	lea	(a6),a4
	move.l	LayBase(a5),a6
	move.l	ib_FirstScreen(a4),d4
.loop	  tst.l	  d4
	beq.s	.err_end
	  move.l	  d4,a0
	  move.l	  sc_NextScreen(a0),d4
	  move.w	  sc_MouseX(a0),d0
	bmi.s	.loop
	  move.w	  sc_MouseY(a0),d1
	bmi.s	.loop
	push.l	a0
	lea	sc_LayerInfo(a0),a0
	just	WhichLayer
	pop.l	d1
	tst.l	d0
	  beq.s	  .end
	move.l	d0,a0
	move.l	lr_Window(a0),d0
.end	popm.l	d4/a0-a1/a4/a6
	rts
.err_end:	moveq	#0,d0
	moveq	#0,d1
	bra	.end

DoWindowToFront:
; Takes d0=^Window or NULL (no effect); a5=GlobalPtr
; scratches nothing
	tst.l	d0
	  beq.s	  .abort
	pushm.l	d0-d1/a0-a1/a6
	move.l	IntBase(a5),a6
	move.l	d0,a0
	move.l	wd_WLayer(a0),d0
	  beq.s	  .end
	move.l	d0,a1
	move.l	lr_ClipRect(a1),d0
	  beq.s	  .end
	move.l	d0,a1
	tst.l	cr_Next(a1)	; Do nothing if it is not covered
	  beq.s	  .end
	move.l	wd_Flags(a0),d0
	and.l	#WFLG_BACKDROP,d0
	  bne.s	  .end	; or if it is a backdrop window
	just	WindowToFront
.end	popm.l	d0-d1/a0-a1/a6
.abort	rts

; Blank or unblank the mouse, depending on Globals.Status.DoMBlank.
; Takes nil (but needs a5=^Globals); Trashes/Returns nil;
; NEVER call with Intbase locked
MouseBlankUpdate:
	pushm.l	a0-a1/a6/d0-d2
	move.l	IntBase(a5),a6
	moveq	#0,d0
	just	LockIBase	; *** Critical section
	move.l	d0,d2
	move.l	Execbase,a6
	just	Forbid	; (make sure no one changes mouse)
	btst	#STB_DoMBlank,Status(a5)
	beq.s	.restore
.blank	  bsr	  BlankMouse
	bra.s	.endif
.restore	  bsr	  RestoreMouse
.endif	just	Permit	; (allow other tasks)
	move.l	d2,a0
	move.l	IntBase(a5),a6
	just	UnlockIBase	; *** End critical section
	popm.l	a0-a1/a6/d0-d2
	rts

;*»»  Blank the current window's mouse pointer
; Takes a5=GlobalPtr; IBase must be locked; Scratches nothing
BlankMouse:	pushm.l	a0-a1/a6/d0-d3
	move.l	IntBase(a5),a6
	move.l	ib_ActiveWindow(a6),a0
	cmp.l	MBlankWindow(a5),a0
	beq.s	.same_wind	; If a different window is now active then
.other_win	  bsr.s	  RestoreMouse	;   unblank it first
	bra.s	.notblanked	; Else
.same_wind	  move.l	  wd_Pointer(a0),d0
	  cmp.l	  MouseChipData(a5),d0 ; If still blanked, give up
	  beq.s	  .end_blank
.notblanked	move.l	a0,MBlankWindow(a5)	; Remember window & its pointer
	  beq.s	  .no_blank	; Quit if no current window(!)
	move.l	wd_WScreen(a0),MBlankScreen(a5)	; Remember screen
	clr.l	OldPointer+Ptr_Image(a5)	; Assume no custom image
	move.l	wd_Pointer(a0),d0
	cmp.l	MouseChipData(a5),d0
	  beq.s	  .end_blank	; Quit if already blanked
	move.l	d0,OldPointer+Ptr_Image(a5)
	move.l	wd_PtrDimensions(a0),OldPointer+Ptr_Dimensions(a5)
	move.l	MouseChipData(a5),a1
	moveq	#1,d0	;height
	moveq	#16,d1	;width
	moveq	#0,d2	;xoffset
	moveq	#0,d3	;yoffset
	just	SetPointer
.end_blank	bset	#STB_MBlanked,Status(a5)
.no_blank	popm.l	a0-a1/a6/d0-d3
	rts

;*»»  Restore old mouse image
; Takes a5=GlobalPtr; IBase must be locked; Scratches nil
RestoreMouse:
	pushm.l	a0-a1/a6/d0-d3
	move.l	IntBase(a5),a6
	move.l	MBlankWindow(a5),d0
	bne.s	.knownblnk	; If nothing is apparently blanked then
.unknwnblnk	  move.l	  ib_ActiveWindow(a6),d0 ; Try the active window
	    beq.s	    .end_RMouse	; (quit if no active window)
	  move.l	  d0,a0
	  clr.l	  OldPointer+Ptr_Image(a5) ;(whose image we don't know)
	  bra.s	  .foundwind
.knownblnk	move.l	MBlankScreen(a5),d2	; We know it's on this screen
	move.l	ib_FirstScreen(a6),d1	; Start with the first screen
.screenloop	beq.s	.end_RMouse	; While screen^ =/= NULL
	  move.l	  d1,a0		;   Get ^ into address reg
	  cmp.l	  d1,d2		;   Compare screen to target
	beq.s	.foundscrn	; Exit if found target screen
	  move.l	  sc_NextScreen(a0),d1	;  Get next screen
	bra.s	.screenloop	; End while
.foundscrn	move.l	sc_FirstWindow(a0),d1	; Start with first window
.windowloop	beq.s	.end_RMouse	; While window^ =/= NULL
	  move.l	  d1,a0
	  cmp.l	  d0,d1		;   Compare window with target
	beq.s	.foundwind	; Exit if this is the window
	  move.l	  wd_NextWindow(a0),d1	;   Next window
	bra.s	.windowloop	; End while
.foundwind	; We now know window exists and that a0=^window
	move.l	wd_Pointer(a0),d0
	cmp.l	MouseChipData(a5),d0
	bne.s	.end_RMouse	; Skip if already unblanked
	  move.l	  OldPointer+Ptr_Image(a5),a1
	  move.l	  a1,d0
	  bne.s	  .restoreold	; If no image pointer then
	    just	    ClearPointer	;   Restore default pointer
	  bra.s	  .end_RMouse	; Else
.restoreold	    move.b	    OldPointer+Ptr_Height(a5),d0
	    move.b	    OldPointer+Ptr_Width(a5),d1
	    move.b	    OldPointer+Ptr_XOffset(a5),d2
	    move.b	    OldPointer+Ptr_YOffset(a5),d3
	    ext.w	    d0
	    ext.w	    d1
	    ext.w	    d2
	    ext.w	    d3
	    just	    SetPointer	;   Restore custom pointer
.end_RMouse	bclr	#STB_MBlanked,Status(a5)
	popm.l	a0-a1/a6/d0-d3
	rts

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

GlobalPtr:	ds.l	1

NorthgateTable:
	;*»»  Assign PageUp/PageDown/Home/End to shift-cursor sequences
	dc.b	$6b,$4f,IEQUALIFIER_LSHIFT
	dc.b	$6c,$4e,IEQUALIFIER_LSHIFT
	dc.b	$6d,$4c,IEQUALIFIER_LSHIFT
	dc.b	$6e,$4d,IEQUALIFIER_LSHIFT
;	;*»»  Change a couple keypad key assignments to be more Amiga-like.
;	dc.b	$5c,$5b,0	;'/' key -> ')' key
;	dc.b	$5d,$5c,0	;'*' key -> '/' key
;	dc.b	$4a,$5d,0	;'-' key -> '*' key
	dc.b	0

ProcName:	dc.b	'Funky baby Mouse process 1.2',0 ;»» Name of child process
InitialLetter	equ	'F'	;»» child's first initial
InputName:	dc.b	'input.device',0
NilName:	dc.b	'NIL:',0
	even

NewProcTags:	; In allocated memory so command can be pure
	dc.l	NP_Entry
EntryPointTag	equ	*-NewProcTags
	dc.l	0
	dc.l	NP_Name
ProcNameTag	equ	*-NewProcTags
	dc.l	0
	dc.l	NP_Cli,-1
	dc.l	NP_Priority,TaskPriority
	dc.l	0	;end of tags

***************************************************************************
EndResidentCode:	;end of code copied into allocated memory
***************************************************************************
***************************************************************************
ResidentCodeLength	equ	EndResidentCode-StartResidentCode


***************************************************************************
* Global data and constants
***************************************************************************

HANDLERPRI		equ	100	;Higher than pesky AMOS
TaskPriority	equ	20
ShellPriority	equ	0

NULL	equ	0
KEYCODE_ESC	equ	$45

	BITDEF	BB,TOP,0
	BITDEF	BB,BOTTOM,1
	BITDEF	BB,LEFT,2
	BITDEF	BB,RIGHT,3

DosName	dc.b	"dos.library",0
IntName	dc.b	"intuition.library",0
LayName	dc.b	"layers.library",0

;*****************************************************************
;* Global data structure definitions

	STRUCTURE	PointerStruct,0
	APTR	Ptr_Image
	LABEL	Ptr_Dimensions
	BYTE	Ptr_Height
	BYTE	Ptr_Width
	BYTE	Ptr_XOffset
	BYTE	Ptr_YOffset
	LABEL	Ptr_SIZEOF

wd_PtrDimensions	equ	wd_PtrHeight

	STRUCTURE	Data,0
	APTR	MouseChipData
	LABEL	FirstLibBase
	APTR	DosBase
	APTR	IntBase
	APTR	LayBase

	LABEL	Sigs
	LONG	CLISig
	LONG	UpdateSig
	LONG	MouseBlankSig
	LONG	ScreenDepthSig
	LONG	CloseScreenSig
	LABEL	EndSigs
NumSigs	equ	(EndSigs-Sigs)/4
	LONG	AllSignals	;Logical OR of all signals

	APTR	Task
	APTR	ArgPtr
	LABEL	OldRoutines
	APTR	OldDisplayBeep
	APTR	OldCloseScreen
	APTR	OldScreenDepth
	LONG	MouseTime	;timeout for mouse blanking
	APTR	MBlankWindow
	APTR	MBlankScreen
	STRUCT	OldPointer,Ptr_SIZEOF
	LONG	ScreenTime	;for screen blanking
	APTR	ClickWindow
	LONG	ClickCount
	STRUCT	ClickTime,TV_SIZE
	APTR	InputMsgPort
	APTR	InputIORequest

Template:	dc.b	'M=MBLANK/K/N,'
	dc.b	'S=SBLANK/K/N,'
	dc.b	'A=ACCELERATION/K/N,'
	dc.b	'T=THRESHOLD/K/N,'
	dc.b	'CTF=CLICKTOFRONT/K/N,'
	dc.b	'CTFB=CLICKTOFRONTBORDER/K/N,'
	dc.b	'CTB=CLICKTOBACK/S,'
	dc.b	'AFS=ACTIVATEFRONTSCREEN/S,'
	dc.b	'CMD=COMMAND/K,'
	dc.b	'SUNMOUSE/S,'
	dc.b	'NORTHGATE/S,'
	dc.b	'NOBEEP/S'
	dc.b	0

	LABEL	Options
	LONG	MBlankOption
	LONG	SBlankOption
	LONG	AccelOption
	LONG	ThreshOption
	LONG	CTFOption
	LONG	ClickBorderOption
	LONG	CTBOption
	LONG	ActivateScreenOption
	LONG	CmdOption
	LONG	SunMouseOption
	LONG	NorthgateOption
	LONG	NoBeepOption
	LABEL	EndOptions
NumOptions	equ	(EndOptions-Options)/4

	LABEL	OptionCaches
	LONG	MouseBlank
	LONG	ScreenBlank
	LONG	Acceleration	; Copies of parameters
	LONG	Threshold	; ..to avoid having to wait on semaphore
	LONG	CTF	; ..which could deadlock input handler
	LONG	CTFB

	STRUCT	InputInterrupt,IS_SIZE
	STRUCT	MySemaphore,SS_SIZE
	STRUCT	ScreenTable,ST_SIZEOF
	WORD	CurrentX
	WORD	CurrentY
	WORD	DampingConstant
	BYTE	Status	; Various status bits (defined below)
	BYTE	Padding	; To fill out to even number of bytes

	STRUCT	ResidentCode,ResidentCodeLength
	LABEL	Data_SIZEOF

;Bit definitions for Status
	BITDEF	ST,SBlanked,0
	BITDEF	ST,MBlanked,1
	BITDEF	ST,SunMouse,2
	BITDEF	ST,HandlerInstalled,3
	BITDEF	ST,InputDeviceOpened,4
	BITDEF	ST,DoMBlank,5

	STRUCTURE	MouseData,12
	LABEL	MouseData_SIZEOF

	END