;QMouse.s
;Written by Dan Babcock

;This source was assembled with Macro68 release 3.

	exeobj
	objfile	'c:QMouse'
	MC68000
	multipass

;Set INFO_LEVEL to 1 for full debugging output.
INFO_LEVEL	equ	0

;Put a message to the serial port.  Used like so:
;
;PUTDEBUG	<'Init: called'>
;
;Parameters can be printed out by pushing them on the stack and
;adding the appropriate C printf-style % formatting commands.

PUTDEBUG 	macro	;<msg>
	ifne	INFO_LEVEL
	movem.l	d0-d1/a0-a1,-(sp)
	lea	.msg\@(pc),a0	;Point to static format string
	lea	16(sp),a1	;Point to args
	bsr	KPutFmt
	movem.l	(sp)+,d0-d1/a0-a1
	bra	.end\@
.msg\@:
	dc.b	\1,$a,0
	even
.end\@:
	endc
	endm


MAXQUEUE	equ	32
SCREENLISTSIZE	equ	32	;maximum number of screens to keep track of
POINTERLISTSIZE	equ	32
HANDLERPRI	equ	60	;priority of input.device handler
TaskPriority	equ	21
ShellPriority	equ	0
MaxCopperSize	equ	500

SysBackground	equ	$e	;offset into gb_copinit copper list
WindowStart	equ	$0	;offset into LOF copper list
DefColor	equ	$fff	;white
WaitLine	equ	$16

	move.l	4,a6
;Allocate global data structure
	move.l	#Data_Sizeof,d0
	move.l	#MEMF_CLEAR,d1
	SYS	AllocMem
	tst.l	d0
	beq	.ErrEnd
		move.l	d0,a5
	lea	GlobalPtr(pc),a0
	move.l	a5,(a0)

;Allocate chip RAM data.
	move.l	#CData_Sizeof,d0
	move.l	#MEMF_CLEAR+MEMF_CHIP,d1
	SYS	AllocMem
	move.l	d0,ChipData(a5)
	beq	CleanUp

;Initialize static data
	move.b	#HANDLERPRI,InputInterrupt+LN_PRI(a5)
	move.l	a5,InputInterrupt+IS_DATA(a5)
	moveq	#1,d0
	move.l	d0,ClickCount(a5)

;Open libraries
	lea	LibTable(pc),a2
	move.l	a2,a3
	moveq	#NumLibs-1,d2
.OLibLoop:
	move.w	(a2)+,a1
	add.l	a3,a1
	SYS	OldOpenLibrary
	move.w	(a2)+,a0
	add.l	a5,a0
	move.l	d0,(a0)
	beq	CleanUp
	dbra	d2,.OLibLoop

;Parse command line
	lea	Template(pc),a0
	move.l	a0,d1
	lea	Options(a5),a0
	move.l	a0,d2
	moveq	#0,d3	;no custom rdarg structure
	move.l	DosBase(a5),a6
	SYS	ReadArgs
	move.l	d0,ArgPtr(a5)
	bne	.ArgsOK
	lea	BadArgs(pc),a0
	move.l	a0,d1
	SYS	VPrintf
	bra	CleanUp

.ErrEnd:	moveq	#RETURN_FAIL,d0
	rts

.ArgsOK:
	move.l	4,a6

;Process QUIT option, if selected.
	tst.l	QuitOption(a5)
	beq	.SkipQuit
	lea	ProcName(pc),a1
	SYS	FindTask
	tst.l	d0
	beq	CleanUp
	move.l	d0,a1
	move.l	TC_Userdata(a1),a0
	move.l	QuitSig(a0),d0
	SYS	Signal	;tell task to quit
	bsr	CleanUp
	bra	.Exit
.SkipQuit:

;If already installed, kill the existing process.
	lea	ProcName(pc),a1
	SYS	FindTask
	tst.l	d0
	beq	.NotInstalled
	move.l	d0,a1
	move.l	TC_Userdata(a1),a0
	move.l	QuitSig(a0),d0
	SYS	Signal	;tell task to quit

	moveq	#26,d2
.Kill:	subq.l	#1,d2
	beq	.Exit
	move.l	DosBase(a5),a6
	moveq	#2,d1
	SYS	Delay
	lea	ProcName(pc),a1
	move.l	4,a6
	SYS	FindTask
	tst.l	d0
	bne	.Kill
.NotInstalled:

;Get stack for command.
	move.l	DosBase(a5),a6
	SYS	Cli
	move.l	d0,a0
	move.l	cli_DefaultStack(a0),d0
	lsl.l	#2,d0
	lea	CmdStackSize(pc),a0
	move.l	d0,(a0)
	move.l	4,a6

;Perform relocations.
	lea	RelocTable(pc),a0
	lea	ResidentCode(a5),a1	;destination
	lea	StartAllocCode(pc),a2
	moveq	#NumRelocs-1,d0
.RLoop:	move.w	(a0)+,d2
	move.l	(a2,d2.w),d1
	add.l	a1,d1
	move.l	d1,(a2,d2.w)
	dbra	d0,.RLoop

;Copy resident part of code into allocated memory
	lea	StartAllocCode(pc),a0	;source
;	lea	ResidentCode(a5),a1	;destination
	move.l	#ResidentCodeSize,d0	;size
	SYS	CopyMem
	SYS	CacheClearU	;for 020/030/040

;Start a process
	move.l	DosBase(a5),a6
	lea	ResidentCode(a5),a0
	lea	EntryPoint(pc),a1
	move.l	a0,(a1)
	lea	ProcName-StartAllocCode+ResidentCode(a5),a0
	lea	ProcNamePtr(pc),a1
	move.l	a0,(a1)
	lea	NewProcTags(pc),a0
	move.l	a0,d1
	SYS	CreateNewProc

;Exit back to CLI
.Exit:	moveq	#0,d0
	rts

RelocTable:
	dc.w	reloc1-StartAllocCode
	dc.w	reloc2-StartAllocCode
	dc.w	reloc3-StartAllocCode
	dc.w	reloc4-StartAllocCode
EndRelocTable:
NumRelocs	equ	(EndRelocTable-RelocTable)/2

***************************************************************************
* The following code is copied into allocated memory.                     *
***************************************************************************

StartAllocCode:
	move.l	4,a6
	move.l	GlobalPtr(pc),a5
	lea	IntRoutine(pc),a0
	move.l	a0,InputInterrupt+IS_CODE(a5)

;Initialize damping constant.
	move.l	AccelOption(a5),d0
	beq	.NoDamp
	move.l	d0,a0
	move.l	(a0),d0
	subq.l	#1,d0
	move.l	ThreshOption(a5),d1
	beq	.DefThresh
	move.l	d1,a0
	move.l	(a0),d1
.DefThresh:
	addq.l	#1,d1
	muls.w	d0,d1
	move.w	d1,DampingConstant(a5)
.NoDamp:

;Allocate signals
	moveq	#NumSigs-1,d2
	lea	Sigs(a5),a2
.SigLoop:	moveq	#-1,d0
	SYS	AllocSignal
	tst.l	d0
	bmi	CleanUp
	moveq	#0,d1
	bset	d0,d1
	move.l	d1,(a2)+
	dbra	d2,.SigLoop

;Initialize semaphore(s)
	lea	Semaphores(a5),a2
	moveq	#NumSemaphores-1,d2
.InitSema:
	move.l	a2,a0
	SYS	InitSemaphore
	lea	SS_SIZE(a2),a2
	dbra	d2,.InitSema

	move.l	ThisTask(a6),a0
	move.l	a0,Task(a5)
	move.l	a5,TC_Userdata(a0)

;Install no-flicker option if selected.
	tst.l	FlickerOption(a5)
	beq	.NoFlicker
	move.l	GraphBase(a5),a0
	move.l	gb_copinit(a0),a0
	move.l	a0,OriginalCopInit(a5)
	move.l	ChipData(a5),a1
	lea	Copper(a1),a1
	move.l	a1,a2
	move.w	#MaxCopperSize/4-1,d0
.CLoop:	move.l	(a0)+,d1
	cmp.l	#$008A0000,d1
	beq	.CopEnd
	move.l	d1,(a1)+
	dbra	d0,.CLoop
	clr.l	FlickerOption(a5)
	bra	.NoFlicker
.CopEnd:	move.l	#$01800000,(a1)+
	move.l	a1,WaitLineAddress(a5)
	move.l	#$1607FFFE,(a1)+
	move.w	#$0180,(a1)+
	move.l	#$008A0000,2(a1)
	move.l	a1,RealBackground(a5)
	move.l	a2,cop1lc+_custom
	move.l	GraphBase(a5),a0
	move.l	a2,gb_copinit(a0)
	cmp.w	#color,SysBackground-2(a2)
	bne	.DefColor
	move.w	SysBackground(a2),(a1)
	bra	.NoFlicker
.DefColor:
	move.w	#DefColor,(a1)
.NoFlicker:

;Patch MANY system routines.
	SYS	Forbid
	lea	PatchTable(pc),a2
	move.l	a2,a3
.Patch:	move.w	(a2)+,d0	;get base
	beq	.EndPatch
	move.l	(a5,d0.w),a1	;library base to patch
	move.w	(a2)+,d0	;get minimum library version
	cmp.w	LIB_VERSION(a1),d0
	bls	.VersionOK
	addq.l	#6,a2
	bra	.Patch
.VersionOK:
	move.w	(a2)+,d0	;get offset to new routine
	lea	(a3,d0.w),a0
	move.l	a0,d0	;new routine address
	move.w	(a2)+,a0	;_LVO offset
	SYS	SetFunction
	move.w	(a2)+,d1
	move.l	d0,(a5,d1.w)
	bra	.Patch
.EndPatch:
	SYS	Permit

;Create an IORequest structure
	SYS	CreateMsgPort
	move.l	d0,InputMsgPort(a5)
	beq	CleanUp
	move.l	d0,a0
	moveq	#IOSTD_SIZE,d0
	SYS	CreateIORequest
	move.l	d0,InputIORequest(a5)
	beq	CleanUp

;Perform NoClick, if desired.
	move.l	NoClickOption(a5),d2
	beq	.Click
	move.l	d2,a0
	move.l	(a0),d2
.Click:	moveq	#0,d3
	moveq	#3,d4
.ClickLoop:
	lea	TrackName(pc),a0	;device name
	move.l	d3,d0	;unit number
	move.l	InputIORequest(a5),a1	;IO request
	moveq	#0,d1	;flags
	SYS	OpenDevice
	tst.l	d0
	bne	.ECLoop
	move.l	InputIORequest(a5),a1	;IO request
	move.l	IO_UNIT(a1),a0
	btst	d3,d2
	beq	.SkipNoClick
	bset	#TDPB_NOCLICK,TDU_PUBFLAGS(a0)
.SkipNoClick:
	tst.l	VerifyOption(a5)
	beq	.NoVerify
	bset	#1,TDU_PUBFLAGS(a0)
.NoVerify:
	tst.l	NoVerifyOption(a5)
	beq	.SkipVerify
	bclr	#1,TDU_PUBFLAGS(a0)
.SkipVerify:
	SYS	CloseDevice
.ECLoop:	addq.l	#1,d3
	dbra	d4,.ClickLoop

;Handle 'STAR' option.
	tst.l	StarOption(a5)
	beq	.NoStar
	move.l	DosBase(a5),a0
	move.l	dl_Root(a0),a0
	bset	#0,rn_Flags(a0)
.NoStar:

;Open the input.device
	lea	InputName(pc),a0	;device name
	moveq	#0,d0	;unit number
	move.l	InputIORequest(a5),a1	;IO request
	moveq	#0,d1	;flags
	SYS	OpenDevice
	tst.l	d0
	bne	CleanUp

;Install virtical blank routine if needed.
	tst.l	SunMouseOption(a5)
	beq	.SkipVBL
	moveq	#INTB_VERTB,d0
	lea	VBlankServer(a5),a1
	move.l	a5,IS_DATA(a1)
	lea	VBlankRoutine(pc),a0
	move.l	a0,IS_CODE(a1)
	move.b	#NT_INTERRUPT,LN_TYPE(a1)
	SYS	AddIntServer
	bset	#STB_IntInstalled,Status(a5)
.SkipVBL:

;Install input handler
	move.l	InputIORequest(a5),a1
	move.w	#IND_ADDHANDLER,IO_COMMAND(a1)
	lea	InputInterrupt(a5),a0
	move.l	a0,IO_DATA(a1)
	SYS	DoIO
	tst.l	d0
	bne	CleanUp
	st	HandlerInstalled(a5)

;Wait for signal(s)
Wait:
	tst.l	TaskQueue(a5)
	bne	.DoTasks
	lea	Sigs(a5),a0
	move.l	(a0)+,d0
	moveq	#NumSigs-2,d1
..	add.l	(a0)+,d0
	dbra	d1,..
	move.l	4,a6
	SYS	Wait
	move.l	d0,d7
	move.l	d7,d1
	and.l	QuitSig(a5),d1
	bne	SafeCleanUp
	move.l	d7,d1
	and.l	FuncSig(a5),d1
	beq	Wait
.DoTasks:	move.l	IntBase(a5),a6
	lea	TaskQueue(a5),a4
.TaskLoop:
	move.l	(a4),d0
	beq	Wait
	clr.l	(a4)+
	move.l	d0,a0
	bsr	GetAllSemaphores
	jsr	(a0)
	bsr	FreeAllSemaphores
	bra	.TaskLoop

BlankScreenTags:
	dc.l	SA_Depth,1
	dc.l	SA_Colors
reloc4:	dc.l	.ColorSpec-StartAllocCode
	dc.l	0	;end tags
.ColorSpec:
	dc.w	0,0,0,0
	dc.w	1,0,0,0
	dc.w	-1
BlankScreenWindowTags:
	dc.l	WA_CustomScreen
BlankScreenTagPtr:
	dc.l	0
	dc.l	0	;end tags

DoBlankScreen:
	move.l	BlankScreenPtr(a5),d0
	beq	.Blank
	move.l	d0,a0
	SYS	ScreenToFront
	move.l	BlankScreenWindowPtr(a5),d0
	beq	_RTS1
	move.l	d0,a0
	jmp	_LVOActivateWindow(a6)
.Blank:	sub.l	a0,a0
	lea	BlankScreenTags(pc),a1
	SYS	OpenScreenTagList
	move.l	d0,BlankScreenPtr(a5)
	beq	_RTS1
	lea	BlankScreenTagPtr(pc),a0
	move.l	d0,(a0)
	sub.l	a0,a0
	lea	BlankScreenWindowTags(pc),a1
	SYS	OpenWindowTagList
	move.l	d0,BlankScreenWindowPtr(a5)
	beq	_RTS1
	move.l	d0,a0
	move.l	ChipData(a5),a1
;	lea	ZeroMouse(a1),a1	;not needed
	moveq	#1,d0	;height
	moveq	#16,d1	;width
	moveq	#0,d2	;xoffset
	moveq	#0,d3	;yoffset
	pea	DoBlankScreen(pc)
	move.l	OldSetPointer(a5),-(sp)
_RTS1:	rts


DoRestoreScreen:
	move.l	BlankScreenPtr(a5),d2
	beq	_RTS1
	clr.l	BlankScreenPtr(a5)
	move.l	BlankScreenWindowPtr(a5),d0
	beq	.SkipWinClose
	clr.l	BlankScreenWindowPtr(a5)
	move.l	d0,a0
	SYS	CloseWindow
.SkipWinClose:
	move.l	d2,a0
	jmp	_LVOCloseScreen(a6)


CTF:	bsr	GetWindow
	tst.l	d0
	beq	_RTS1
	move.l	CTFOption(a5),a2
	move.l	(a2),d1
	cmp.l	#1,d1
	bhi	.MoreThanOne
	bra	DoWindowToFront
.MoreThanOne:
	cmp.l	ClickWindow(a5),d0
	beq	.SameWindow
	move.l	d0,ClickWindow(a5)
	moveq	#1,d0
	move.l	d0,ClickCount(a5)
.Time:	bsr	GetIBase
	move.l	ib_Seconds(a6),ClickTime+TV_SECS(a5)
	move.l	ib_Micros(a6),ClickTime+TV_MICRO(a5)
	bra	FreeIBase
.SameWindow:

;Check to see whether MaxClickDelay has been exceeded. If not, increment
;ClickCount and compare with (CTFOption). If equal, do a WindowToFront.
;Window under pointer in D0.

	push	d0
	move.l	ClickTime+TV_SECS(a5),d0
	move.l	ClickTime+TV_MICRO(a5),d1
	bsr	GetIBase
	move.l	ib_Seconds(a6),d2
	move.l	ib_Micros(a6),d3
	bsr	FreeIBase
	SYS	DoubleClick
	tst.l	d0
	beq	.TimeExceeded
	addq.l	#1,ClickCount(a5)
	move.l	CTFOption(a5),a0
	move.l	(a0),d0
	cmp.l	ClickCount(a5),d0
	bhi	.NeedMoreClicks
	clr.l	ClickCount(a5)
	pop	d0
	bra	DoWindowToFront
.TimeExceeded:
	moveq	#1,d0
	move.l	d0,ClickCount(a5)
.NeedMoreClicks:
	pop	d0
	bra	.Time

CTB:	bsr	GetWindow
	tst.l	STBOption(a5)
	bne	.FlipScreen
	tst.l	d0
	beq	.FlipScreen
	move.l	d0,a1
	move.l	wd_Flags(a1),d2
	and.l	#WFLG_BACKDROP,d2
	bne	.FlipScreen

;Is this the only window on this screen, except for backdrop windows?
	move.l	wd_WScreen(a1),a1
	move.l	sc_FirstWindow(a1),a1
.BackLoop:
	cmp.l	a1,d0
	beq	.EBackLoop
	move.l	wd_Flags(a1),d2
	and.l	#WFLG_BACKDROP,d2
	beq	.FlipWindow
.EBackLoop:
	move.l	(a1),a1
	move.l	a1,d2
	bne	.BackLoop
.FlipScreen:
	tst.l	d1
	beq	.End
	move.l	d1,a0
	jmp	_LVOScreenToBack(a6)
.FlipWindow:
	move.l	d0,a0
	jmp	_LVOWindowToBack(a6)
.End:	rts

PopCLI:
;Execute the desired command
	move.l	a6,-(sp)
	move.l	DosBase(a5),a6
	move.l	CmdOption(a5),d1
	beq	.End
	lea	.Tags(pc),a0
	move.l	a0,d2	;tags

;Important fix: free the semaphores (we don't need them here and they can and
;do lead to deadlocks).
	bsr	FreeAllSemaphores
	SYS	SystemTagList
	bsr	GetAllSemaphores

	tst.l	NoWBenchToFrontOption(a5)
	bne	.End
	move.l	IntBase(a5),a6
	SYS	WBenchToFront
.End:	move.l	(sp)+,a6
	rts

.Tags:	dc.l	SYS_UserShell,0
	dc.l	NP_Priority,ShellPriority
	dc.l	NP_StackSize
CmdStackSize:
	dc.l	0
	dc.l	0	;end of tag list

SafeCleanUp:
;Make sure the SetFunction'ed vectors are still intact.
	lea	PatchTable(pc),a0
	move.l	a0,a2
.Safe:	move.w	(a0)+,d0
	beq	CleanUp
	move.l	(a5,d0.w),a1	;library base
	addq.l	#2,a0	;skip version #
	move.w	(a0)+,d0
	lea	(a2,d0.w),a3	;address of new routine
	move.w	(a0)+,d0	;_LVO offset
	move.w	(a0)+,d1	;Old vector
	tst.l	(a5,d1.w)
	beq	.Safe
	cmp.l	2(a1,d0.w),a3
	beq	.Safe

;A routine has been patched by another program. Alert the user.
;a1 is lib pointer, d0.w is routine offset.

	ext.l	d0
	neg.l	d0
	move.l	d0,-(sp)
	move.l	LN_NAME(a1),-(sp)
	move.l	sp,a3
	move.l	IntBase(a5),a6
	sub.l	a0,a0
	lea	PatchAlert(pc),a1
	sub.l	a2,a2
	SYS	EasyRequestArgs
	addq.l	#8,sp
	bra	Wait
PatchAlert:
	dc.l	es_SIZEOF
	dc.l	0
reloc1:	dc.l	TitleText-StartAllocCode
reloc2:	dc.l	AlertText-StartAllocCode
reloc3:	dc.l	OkayText-StartAllocCode
TitleText:
	dc.b	'QMouse message',0
AlertText:
	dc.b	'Can''t quit or modify parameters. Another program',$a
	dc.b	'has patched the same OS routines!',$a
	dc.b	$a
	dc.b	'Technical information:',$a
	dc.b	'Library: %s',$a
	dc.b	'Offset:  -$%lx',$a
	dc.b	'(There may be others)',0
OkayText:
	dc.b	'OK',0
	even

CleanUp:

;Enter with global ptr in A5

	move.l	4,a6

;Remove interrupt routine, if installed.
	btst	#STB_IntInstalled,Status(a5)
	beq	.SkipIntRemove
	moveq	#INTB_VERTB,d0
	lea	VBlankServer(a5),a1
	SYS	RemIntServer
.SkipIntRemove:

;Remove input handler, if installed.
	tst.b	HandlerInstalled(a5)
	beq	.SkipHand
	move.l	InputIORequest(a5),a1
	move.w	#IND_REMHANDLER,IO_COMMAND(a1)
	lea	InputInterrupt(a5),a0
	move.l	a0,IO_DATA(a1)
	SYS	DoIO
.SkipHand:

	tst.l	OldCloseScreen(a5)
	beq	.SkipRestore

	bsr	RestoreMouse
	move.l	IntBase(a5),a6
	bsr	DoRestoreScreen
	move.l	4,a6

;Restore original vectors.

	lea	PatchTable(pc),a2
.RestoreVecs:
	move.w	(a2),d0
	beq	.SkipRestore
	addq.l	#6,a2	;skip new routine info
	move.l	(a5,d0.w),a1	;get library base
	move.w	(a2)+,a0	;get _LVO
	move.w	(a2)+,d0
	move.l	(a5,d0.w),d0	;old routine
	beq	.RestoreVecs
	SYS	SetFunction
	bra	.RestoreVecs
.SkipRestore:

;Delay for 1/10 second to ensure that no-one is using the patch code.
	move.l	DosBase(a5),d0
	beq	.SkipFreeArg
	move.l	d0,a6
	moveq	#5,d1
	SYS	Delay

;Free argument structure
	move.l	ArgPtr(a5),d1
	beq	.SkipFreeArg
	SYS	FreeArgs
.SkipFreeArg:

	move.l	4,a6

;Free signals
	lea	Sigs(a5),a2
	moveq	#NumSigs-1,d2
.SigLoop:	move.l	(a2)+,d1
	beq	.NextSig
	moveq	#-1,d0
.BitLoop:	addq.l	#1,d0
	btst	d0,d1
	beq	.BitLoop
	SYS	FreeSignal
.NextSig:	dbra	d2,.SigLoop

;Remove flicker option, if selected.
	tst.l	FlickerOption(a5)
	beq	.SkipFlicker
	move.l	OriginalCopInit(a5),a0
	move.l	GraphBase(a5),a2
	move.l	gb_copinit(a2),a1
.CLoop:	move.l	(a1)+,(a0)+
	cmp.l	#$008A0000,(a0)
	bne	.CLoop
	move.l	OriginalCopInit(a5),gb_copinit(a2)
	move.l	OriginalCopInit(a5),cop1lc+_custom
.SkipFlicker:

;Close libraries
	lea	LibTable+2(pc),a2
	moveq	#NumLibs-1,d2
.CLibLoop:
	move.w	(a2),a0
	add.l	a5,a0
	tst.l	(a0)
	beq	.SkipClose
	move.l	(a0),a1
	SYS	CloseLibrary
.SkipClose:
	addq.l	#4,a2
	dbra	d2,.CLibLoop

;Close input.device
	move.l	InputIORequest(a5),d0
	beq	.SkipCloseDev
	move.l	d0,a1
	tst.b	IO_ERROR(a1)	;was there an error?
	bne	.SkipCloseDev	;yes, don't close
	SYS	CloseDevice
.SkipCloseDev:

;Delete IORequest structure
	move.l	InputIORequest(a5),a0
	SYS	DeleteIORequest
	move.l	InputMsgPort(a5),a0
	SYS	DeleteMsgPort

;Free chip data.
	move.l	ChipData(a5),d0
	beq	.SkipChipData
	move.l	d0,a1
	move.l	#CData_Sizeof,d0
	SYS	FreeMem
.SkipChipData:

;Deallocate global structure. Note that this code is (sometimes) executed
;within the memory that we are freeing, so we are careful to terminate with
;a JMP.
	move.l	a5,a1
	move.l	#Data_Sizeof,d0
	jmp	_LVOFreeMem(a6)

;Semaphore routines

GetIBase:
;	PUTDEBUG	<'GetIBase: Called'>
	movem.l	d0-d1/a0-a1/a5-a6,-(sp)
	move.l	GlobalPtr(pc),a5
	move.l	IntBase(a5),a6
	moveq	#0,d0
	SYS	LockIBase
	move.l	d0,IBaseLock(a5)
	movem.l	(sp)+,d0-d1/a0-a1/a5-a6
;	PUTDEBUG	<'GetIBase: Done'>
	rts
FreeIBase:
;	PUTDEBUG	<'FreeIBase'>
	movem.l	d0-d1/a0-a1/a6,-(sp)
	move.l	GlobalPtr(pc),a0
	move.l	IntBase(a0),a6
	move.l	IBaseLock(a0),a0
	SYS	UnlockIBase
	movem.l	(sp)+,d0-d1/a0-a1/a6
	rts


GetSemaphore:
	PUTDEBUG	<'GetSemaphore: Called'>
	movem.l	a0/a6,-(sp)
	move.l	GlobalPtr(pc),a0
	lea	MySemaphore(a0),a0
_GS:	move.l	4,a6
	SYS	ObtainSemaphore
	movem.l	(sp)+,a0/a6
	PUTDEBUG	<'GetSemaphore: Done'>
	rts
FreeSemaphore:
	movem.l	a0/a6,-(sp)
	move.l	GlobalPtr(pc),a0
	lea	MySemaphore(a0),a0
_FS:	move.l	4,a6
	SYS	ReleaseSemaphore
	movem.l	(sp)+,a0/a6
	PUTDEBUG	<'FreeSemaphore'>
	rts

GetCloseWindowSemaphore:
	PUTDEBUG	<'GetCloseWindowSemaphore'>
	movem.l	a0/a6,-(sp)
	move.l	GlobalPtr(pc),a0
	lea	CloseWindowSemaphore(a0),a0
	bra	_GS
FreeCloseWindowSemaphore:
	PUTDEBUG	<'FreeCloseWindowSemaphore'>
	movem.l	a0/a6,-(sp)
	move.l	GlobalPtr(pc),a0
	lea	CloseWindowSemaphore(a0),a0
	bra	_FS

GetCloseScreenSemaphore:
	PUTDEBUG	<'GetCloseScreenSemaphore'>
	movem.l	a0/a6,-(sp)
	move.l	GlobalPtr(pc),a0
	lea	CloseScreenSemaphore(a0),a0
	bra	_GS
FreeCloseScreenSemaphore:
	PUTDEBUG	<'FreeCloseScreenSemaphore'>
	movem.l	a0/a6,-(sp)
	move.l	GlobalPtr(pc),a0
	lea	CloseScreenSemaphore(a0),a0
	bra	_FS

GetCloseWBSemaphore:
	PUTDEBUG	<'GetCloseWBSemaphore'>
	movem.l	a0/a6,-(sp)
	move.l	GlobalPtr(pc),a0
	lea	CloseWBSemaphore(a0),a0
	bra	_GS
FreeCloseWBSemaphore:
	PUTDEBUG	<'FreeCloseWBSemaphore'>
	movem.l	a0/a6,-(sp)
	move.l	GlobalPtr(pc),a0
	lea	CloseWBSemaphore(a0),a0
	bra	_FS

GetAllSemaphores:
;The order matters! The sems must be allocated in this order to avoid
;the "deadly embrace".
	bsr	GetCloseWBSemaphore
	bsr	GetCloseScreenSemaphore
	bsr	GetCloseWindowSemaphore
	bra	GetSemaphore
FreeAllSemaphores:
;We can free in any order (provided that we are freeing ALL of them).
	bsr	FreeSemaphore
	bsr	FreeCloseWindowSemaphore
	bsr	FreeCloseScreenSemaphore
	bra	FreeCloseWBSemaphore


LibTable:	dc.w	DosName-LibTable
	dc.w	DosBase
	dc.w	IntName-LibTable
	dc.w	IntBase
	dc.w	LayersName-LibTable
	dc.w	LayersBase
	dc.w	GraphName-LibTable
	dc.w	GraphBase
	dc.w	UtilityName-LibTable
	dc.w	_UtilityBase
EndLibTable:

NumLibs	equ	(EndLibTable-LibTable)/4

;****************** SetFunction'ed routines ***************

patch	macro
	dc.w	\1
	dc.w	\3	;minimum library version
	dc.w	New\2-PatchTable
	dc.w	_LVO\2
	dc.w	Old\2
	endm

PatchTable:
	patch	IntBase,SetWindowPointerA,39
	patch	IntBase,OpenWindowTagList,39
	patch	IntBase,CloseScreen,37
	patch	IntBase,CloseWorkBench,37
	patch	IntBase,CloseWindow,37
	patch	IntBase,SetPointer,37
	patch	IntBase,ClearPointer,37
	patch	IntBase,DisplayBeep,37
	dc.w	0

NewDisplayBeep:
	PUTDEBUG	<'NewDisplayBeep'>
	move.l	GlobalPtr(pc),a1
	tst.l	BeepOption(a1)
	bne	.SkipBeep
	move.l	OldDisplayBeep(a1),-(sp)
.SkipBeep:
	rts

NewOpenWindowTagList:
;Enter with NewWindow in A0, taglist in A1
	PUTDEBUG	<'NewOpenWindowTagList'>
	move.l	a1,-(sp)
	pea	.HandlePointer(pc)
	subq.l	#8,sp
	move.l	a0,(sp)
	move.l	GlobalPtr(pc),a0
	move.l	OldOpenWindowTagList(a0),4(sp)
	move.l	(sp)+,a0
.End:	rts
.RestoreD0:
	move.l	(sp)+,d0
	rts
.HandlePointer:
	move.l	(sp)+,a1
	tst.l	d0
	beq	.End
	move.l	d0,a0
	move.l	d0,-(sp)
	pea	.RestoreD0(pc)
;	bra	NewSetWindowPointerA

NewSetWindowPointerA:
;Enter with:
;A0 - Window
;A1 - taglist

SaveRegs	setrl	d4-d5/a0-a6

	PUTDEBUG	<'NewSetWindowPointerA'>
	bsr	GetSemaphore
	movem.l	SaveRegs,-(sp)
	move.l	GlobalPtr(pc),a5
	lea	PointerList(a5),a2

;Find if window is already in table and find empty spot.
	moveq	#POINTERLISTSIZE-1,d4
	sub.l	a3,a3
.WinSearch:
	move.l	pt_Window(a2),d5
	bne	.SkipSave
	move.l	a2,a3
.SkipSave:
	cmp.l	d5,a0
	beq	.Found
	lea	pt_Sizeof(a2),a2
	dbra	d4,.WinSearch
	move.l	a3,a2
	move.l	a2,d4
	beq	.CallOld	;no room in table, so forget it
.Found:
;Store the taglist information
	move.l	a0,a3	;window
	move.l	a1,a4	;taglist
	move.l	a0,pt_Window(a2)
	st	TagVersion(a2)
	move.l	_UtilityBase(a5),a6
;Check for WA_BusyPointer.
	move.l	#WA_BusyPointer,d0
	move.l	a4,a0
	SYS	FindTagItem
	tst.l	d0
	beq	.NotBusy
	move.l	d0,a0

	IFNE	INFO_LEVEL
	move.l	4(a0),-(sp)
	move.l	(a0),-(sp)
	PUTDEBUG	<'WA_BusyPointer=%lx,%lx '>
	addq.l	#8,sp
	ENDC

	move.l	(a0),(a2)
	move.l	4(a0),4(a2)
	beq	.NotBusy
	clr.l	8(a2)
	bra	.CallOld
.NotBusy:
;Check for WA_Pointer.
	move.l	#WA_Pointer,d0
	move.l	a4,a0
	SYS	FindTagItem
	tst.l	d0
	beq	.Nothing
	move.l	d0,a0

	IFNE	INFO_LEVEL
	move.l	4(a0),-(sp)
	move.l	(a0),-(sp)
	PUTDEBUG	<'WA_Pointer=%lx,%lx '>
	addq.l	#8,sp
	ENDC

	move.l	(a0),(a2)
	move.l	4(a0),4(a2)
	clr.l	8(a2)
	bra	.CallOld
.Nothing:
;Don't understand the taglist.
	PUTDEBUG	<'Don''t grok SetWindowPointerA taglist!'>
	clr.l	pt_Window(a2)
.CallOld:	cmp.l	MBlankWindow(a5),a3
	beq	.End
;Call old SetWindowPointerA
	movem.l	(sp)+,SaveRegs
	subq.l	#8,sp
	move.l	a0,(sp)
	move.l	GlobalPtr(pc),a0
	move.l	OldSetWindowPointerA(a0),4(sp)
	move.l	(sp)+,a0
	bra	FreeSemaphore
.End:	movem.l	(sp)+,SaveRegs
	bra	FreeSemaphore

NewSetPointer:
	PUTDEBUG	<'NewSetPointer'>
	bsr	GetSemaphore
	movem.l	d4-d5/a2-a3/a5,-(sp)
	move.l	GlobalPtr(pc),a5
	cmp.l	ChipData(a5),a1	;special check to ward off sneaky code like
	beq	.End1		;reqtools
	lea	PointerList(a5),a2

;Find if window is already in table and find empty spot.
	moveq	#POINTERLISTSIZE-1,d4
	sub.l	a3,a3
.WinSearch:
	move.l	pt_Window(a2),d5
	bne	.SkipSave
	move.l	a2,a3
.SkipSave:
	cmp.l	d5,a0
	beq	.Found
	lea	pt_Sizeof(a2),a2
	dbra	d4,.WinSearch
	move.l	a3,a2
	move.l	a2,d4
	beq	.End	;no room in table, so forget it
.Found:	movem.l	d0-d3/a0-a1,(a2)
	clr.b	TagVersion(a2)
.End:	cmp.l	MBlankWindow(a5),a0
	beq	.End1
	movem.l	(sp)+,d4-d5/a2-a3/a5
	subq.l	#8,sp
	move.l	a0,(sp)
	move.l	GlobalPtr(pc),a0
	move.l	OldSetPointer(a0),4(sp)
	move.l	(sp)+,a0
	bra	FreeSemaphore
.End1:	movem.l	(sp)+,d4-d5/a2-a3/a5
	bra	FreeSemaphore

NewClearPointer:
	PUTDEBUG	<'NewClearPointer'>
	bsr	GetSemaphore
	move.l	GlobalPtr(pc),a1
	lea	PointerList(a1),a1
	moveq	#POINTERLISTSIZE-1,d0
.ClearLoop:
	cmp.l	pt_Window(a1),a0
	beq	.Found
	lea	pt_Sizeof(a1),a1
	dbra	d0,.ClearLoop
	bra	.End
.Found:	clr.l	pt_Window(a1)
.End:	move.l	GlobalPtr(pc),a1
	cmp.l	MBlankWindow(a1),a0
	beq	.End1
	move.l	OldClearPointer(a1),-(sp)
.End1:	bra	FreeSemaphore

NewCloseWorkBench:
	PUTDEBUG	<'NewCloseWorkBench'>
	bsr	GetCloseWBSemaphore
	SYS	OpenWorkBench	;get workbench screen ptr in d0
	tst.l	d0
	beq	FreeCloseWBSemaphore
	move.l	d0,a0
	move.l	GlobalPtr(pc),a1
	move.l	OldCloseWorkBench(a1),d1
	pea	.Free(pc)
	bra	CloseScreenCode
.Free:	bra	FreeCloseWBSemaphore

NewCloseScreen:
	PUTDEBUG	<'NewCloseScreen'>
	bsr	GetCloseScreenSemaphore
	move.l	GlobalPtr(pc),a1
	move.l	OldCloseScreen(a1),d1
	pea	.Free(pc)
	bra	CloseScreenCode
.Free:	bra	FreeCloseScreenSemaphore

CloseScreenCode:
;Code shared by NewCloseScreen and NewCloseWorkBench
;Enter with Screen in A0, Global in A1, old vector in D1

	bsr	GetSemaphore
	lea	ScreenList(a1),a1
	moveq	#SCREENLISTSIZE-1,d0
.Loop:	cmp.l	(a1),a0
	beq	.Match
	addq.l	#8,a1
	dbra	d0,.Loop
.End:	bsr	FreeSemaphore
	move.l	GlobalPtr(pc),a1
	pea	.ExtraClose(pc)
	move.l	d1,-(sp)
	rts
.Match:	clr.l	(a1)
	bra	.End
.ExtraClose:
	move.l	GlobalPtr(pc),a0
	bsr	GetIBase
	move.l	ib_FirstScreen(a6),CurrentTopScreen(a0)
	move.l	ib_ActiveScreen(a6),CurrentActiveScreen(a0)
	bsr	FreeIBase
	bra	RestoreWindow

NewCloseWindow:
	PUTDEBUG	<'NewCloseWindow'>
	move.l	GlobalPtr(pc),a1
	bsr	GetCloseWindowSemaphore
	bsr	GetSemaphore
	pea	.ExtraClose(pc)
	push	a0
	SYS	ClearPointer
	pop	a0
	move.l	GlobalPtr(pc),a1
	cmp.l	MBlankWindow(a1),a0
	bne	.SkipClearMBW
	clr.l	MBlankWindow(a1)
.SkipClearMBW:
	cmp.l	ClickWindow(a1),a0
	bne	.SkipClearCW
	clr.l	ClickWindow(a1)
.SkipClearCW:
	lea	ScreenList+4(a1),a1
	moveq	#SCREENLISTSIZE-1,d0
.Loop:	cmp.l	(a1),a0
	beq	.Match
	addq.l	#8,a1
	dbra	d0,.Loop
.End:	bsr	FreeSemaphore
	move.l	GlobalPtr(pc),a1
	move.l	OldCloseWindow(a1),-(sp)
	rts
.Match:	clr.l	-4(a1)
	bra	.End
.ExtraClose:
	move.l	GlobalPtr(pc),a0
	bsr	GetIBase
	move.l	ib_ActiveWindow(a6),CurrentActiveWindow(a0)
	bsr	FreeIBase
	bra	FreeCloseWindowSemaphore

;****************** End of SetFunction'ed routines ***************

RememberWindow:

;No OS calls
;Enter with Screen in A0, Window in A2

	movem.l	d0-d1/a0-a2/a5,-(sp)
	move.l	GlobalPtr(pc),a5

;Sanity checks
	move.l	a0,d0
	beq	.End
	move.l	a2,d0
	beq	.End

	lea	ScreenList(a5),a1
	moveq	#SCREENLISTSIZE-1,d0
.ScLoop:	cmp.l	(a1),a0
	beq	.Match
	addq.l	#8,a1
	dbra	d0,.ScLoop

;Screen not found. Add to screen list.
	lea	ScreenList(a5),a1
	moveq	#SCREENLISTSIZE-1,d0
.AddLoop:	tst.l	(a1)
	beq	.Match
	addq.l	#8,a1
	dbra	d0,.AddLoop
	bra	.End	;table is full
.Match:
	move.l	a0,(a1)+	;screen
	move.l	a2,(a1)	;currently activated window
.End:	movem.l	(sp)+,d0-d1/a0-a2/a5
	rts

RestoreWindow:
	movem.l	a1-a2,-(sp)
	move.l	GlobalPtr(pc),a1
	lea	TaskRestoreWindow(pc),a2
	bsr	DoFunc
	movem.l	(sp)+,a1-a2
	rts

TaskRestoreWindow:

;This routine restores (re-activates) the previously activated window for
;this screen.

	movem.l	d0-d2/a0-a2/a6,-(sp)
	move.l	GlobalPtr(pc),a0
	tst.l	NoActivateOption(a0)
	bne	.End
	lea	ScreenList(a0),a2
	move.l	IntBase(a0),a6
	bsr	GetIBase
	move.l	ib_FirstScreen(a6),d2
	bsr	FreeIBase
	tst.l	d2
	beq	.End	;sanity check
	move.l	d2,CurrentTopScreen(a0)
	tst.l	PubChangeOption(a0)
	beq	.NoPubChange

;If the screen is public, make it the default public screen.

	SYS	LockPubScreenList
.PubLoop:	tst.l	d0
	beq	.EndPubChange
	move.l	d0,a0
	cmp.l	psn_Screen(a0),d2
	beq	.FoundScreen
	move.l	LN_SUCC(a0),d0
	bra	.PubLoop	
.FoundScreen:
	move.l	LN_NAME(a0),a0
	SYS	SetDefaultPubScreen

.EndPubChange:
	SYS	UnlockPubScreenList
.NoPubChange:
	move.l	d2,a0
	moveq	#SCREENLISTSIZE-1,d0
.Loop:	cmp.l	(a2),a0
	beq	.Match
	addq.l	#8,a2
	dbra	d0,.Loop
	move.l	sc_FirstWindow(a0),a0	;default window
	move.l	a0,d0
	beq	.End	;sanity check
	bra	.SkipMatch
.Match:
	move.l	4(a2),a0
.SkipMatch:
	SYS	ActivateWindow
	move.l	GlobalPtr(pc),a0
	bsr	GetIBase
	move.l	ib_ActiveScreen(a6),CurrentActiveScreen(a0)
	bsr	FreeIBase
.End:	movem.l	(sp)+,d0-d2/a0-a2/a6
	rts


;Get window associated with current mouse position.
;Returns Window in D0 and screen in D1.

GetWindow:
	movem.l	d4/a0-a1/a4-a6,-(sp)
	move.l	GlobalPtr(pc),a5
	move.l	LayersBase(a5),a6
	move.l	IntBase(a5),a4
	bsr	GetIBase
	move.l	ib_FirstScreen(a4),d4
.ScLoop:	tst.l	d4
	beq	.ErrEnd
	move.l	d4,a0
	move.l	sc_NextScreen(a0),d4
	move.w	sc_MouseX(a0),d0
	bmi	.ScLoop
	move.w	sc_MouseY(a0),d1
	bmi	.ScLoop
	push	a0
	lea	sc_LayerInfo(a0),a0
	bsr	FreeIBase
	SYS	WhichLayer
	pop	d1
	tst.l	d0
	beq	.End
	move.l	d0,a0
	move.l	lr_Window(a0),d0
.End:	movem.l	(sp)+,d4/a0-a1/a4-a6
	rts
.ErrEnd:	bsr	FreeIBase
	moveq	#0,d0
	moveq	#0,d1
	bra	.End

ActivateWindow:

;Activate the window under the mouse pointer.
;Enter with IntuitionBase in A6

	bsr	GetWindow
	tst.l	d0
	beq	.End
	move.l	d0,a0
	bsr	GetIBase
	move.l	ib_ActiveWindow(a6),d0
	bsr	FreeIBase
	cmp.l	d0,a0
	beq	.End
	tst.l	wd_FirstRequest(a0)
	bne	.End
	SYS	ActivateWindow
.End:	rts

DoWindowToFront:

;Perform a WindowToFront on the window in D0.
;Enter with IntuitionBase in A6.

	tst.l	d0
	beq	.End1
	movem.l	d0-d1/a0-a1/a6,-(sp)
	move.l	d0,a0
	move.l	wd_WLayer(a0),a1
	move.l	a1,d0
	beq	.End
	move.l	lr_ClipRect(a1),a1
	move.l	a1,d0
	beq	.End
	tst.l	(a1)
	beq	.End
	move.l	wd_Flags(a0),d0
	and.l	#WFLG_BACKDROP,d0
	bne	.End
	SYS	WindowToFront
.End:	movem.l	(sp)+,d0-d1/a0-a1/a6
.End1:	rts

BlankMouse:
	movem.l	d0-d3/a0-a1/a5-a6,-(sp)
	move.l	GlobalPtr(pc),a5
	move.l	IntBase(a5),a6
	bsr	GetIBase
	move.l	ib_ActiveWindow(a6),d0
	bsr	FreeIBase
	tst.l	d0
	beq	.End
	cmp.l	MBlankWindow(a5),d0
	beq	.End
	bsr	RestoreMouse
	move.l	d0,MBlankWindow(a5)
	move.l	d0,a0
	move.l	ChipData(a5),a1
;	lea	ZeroMouse(a1),a1	;not needed
	moveq	#1,d0	;height
	moveq	#16,d1	;width
	moveq	#0,d2	;xoffset
	moveq	#0,d3	;yoffset
	pea	.End(pc)
	move.l	OldSetPointer(a5),-(sp)
	rts
.End:	movem.l	(sp)+,d0-d3/a0-a1/a5-a6
	rts

RestoreMouse:
	movem.l	d0-d1/a0-a2/a5-a6,-(sp)
	move.l	GlobalPtr(pc),a5
	move.l	IntBase(a5),a6
	move.l	MBlankWindow(a5),d0
	beq	.End

;Look for window in pointer table. If present, do a SetPointer or SetWindowPointerA
;to restore the custom pointer. Otherwise call ClearPointer.

	move.l	d0,a0
	clr.l	MBlankWindow(a5)
	lea	PointerList(a5),a1
	moveq	#POINTERLISTSIZE-1,d0
.WinLoop:	cmp.l	pt_Window(a1),a0
	beq	.Found
	lea	pt_Sizeof(a1),a1
	dbra	d0,.WinLoop
	pea	.End(pc)
	move.l	OldClearPointer(a5),-(sp)
	rts
.End:	movem.l	(sp)+,d0-d1/a0-a2/a5-a6
	rts
.Found:	move.l	a1,a2
	tst.b	TagVersion(a2)
	beq	.NoTag
;Call SetWindowPointerA to restore the custom pointer.
	move.l	pt_Window(a2),a0
	pea	.End(pc)
	move.l	OldSetWindowPointerA(a5),-(sp)
	rts
.NoTag:	movem.l	(a2),d0-d3/a0-a1
	pea	.End(pc)
	move.l	OldSetPointer(a5),-(sp)
	rts

;***************************************************************************
;Start of input handler code
;***************************************************************************

BlankScreen:

;Enter with global in A1.
	bset	#STB_SBlanked,Status(a1)
	lea	DoBlankScreen(pc),a2
	bra	DoFunc

RestoreScreen:

;Enter with global in A1 AND input event in A0.
	move.l	ie_TimeStamp(a0),ScreenTime(a1)
	bclr	#STB_SBlanked,Status(a1)
	beq	_RTS
	lea	DoRestoreScreen(pc),a2
;	bra	DoFunc

DoFunc:

;Enter with pointer to routine in A2
;Enter with global ptr in A1.

	movem.l	d0-d1/a0-a1/a6,-(sp)
	lea	TaskQueue(a1),a0
	moveq	#MAXQUEUE-1,d0
..	tst.l	(a0)+
	dbeq	d0,..
	bne	.End
	move.l	a2,-(a0)
	move.l	FuncSig(a1),d0
	move.l	Task(a1),a1
	move.l	4,a6
	SYS	Signal
.End:	movem.l	(sp)+,d0-d1/a0-a1/a6
_RTS:	rts

;**************************** Input handler *********************************

IntRoutine:
	movem.l	d2/a2/a6,-(sp)
	move.l	a0,-(sp)
	move.l	IntBase(a1),a6
	bsr	GetIBase

;Handle NoFlicker option
	tst.l	FlickerOption(a1)
	beq	.SkipFlick
	move.l	GraphBase(a1),a0
	move.l	gb_LOFlist(a0),a2
	move.l	WaitLineAddress(a1),a0
	cmp.b	#WaitLine-1,WindowStart(a2)
	blo	.DisableNF
	move.b	#WaitLine,(a0)
	bra	.SkipDisableNF
.DisableNF:
	clr.b	(a0)
.SkipDisableNF:
	move.l	GraphBase(a1),a0
	move.l	gb_copinit(a0),a0
	cmp.w	#color,SysBackground-2(a0)
	bne	.DefColor
	move.w	SysBackground(a0),d0
	move.l	RealBackground(a1),a0
	move.w	d0,(a0)
	bra	.SkipColor
.DefColor:
	move.l	RealBackground(a1),a0
	move.w	#DefColor,(a0)
.SkipColor:
	move.l	(sp),a0
.SkipFlick:

;Check for change in active screen.
;But first: attempt to obtain the semaphores
	movem.l	a0-a1/a6,-(sp)
	move.l	4,a6
	lea	Semaphores(a1),a2
	moveq	#NumSemaphores-1,d2
.Attempt:	move.l	a2,a0
	SYS	AttemptSemaphore
	tst.l	d0
	beq	.AttemptFailed
	lea	SS_SIZE(a2),a2
	dbra	d2,.Attempt
	movem.l	(sp)+,a0-a1/a6
	bra	.CheckScreenStuff
.AttemptFailed:
	moveq	#2,d0
	sub.w	d2,d0
	bmi	.NoneSucceeded
	move.l	a2,a0
.CleanUp:	lea	-SS_SIZE(a0),a0
	SYS	ReleaseSemaphore
	dbra	d0,.CleanUp
.NoneSucceeded:
	movem.l	(sp)+,a0-a1/a6
	bra	.NoSemaphore

.CheckScreenStuff:
	move.l	CurrentActiveScreen(a1),d0
	cmp.l	ib_ActiveScreen(a6),d0
	beq	.SameActiveScreen
	push	a0
	move.l	CurrentActiveScreen(a1),a0
	move.l	CurrentActiveWindow(a1),a2
	bsr	RememberWindow
	pop	a0
.SameActiveScreen:

;Check for change in top screen.
	move.l	CurrentTopScreen(a1),d0
	beq	.CSZ
	cmp.l	ib_FirstScreen(a6),d0
	beq	.SkipRW
	push	a0
	move.l	CurrentActiveScreen(a1),a0
	move.l	CurrentActiveWindow(a1),a2
	bsr	RememberWindow
	pop	a0
	lea	TaskRestoreWindow(pc),a2
	bsr	DoFunc
.CSZ:	move.l	ib_FirstScreen(a6),CurrentTopScreen(a1)
.SkipRW:	move.l	ib_ActiveWindow(a6),CurrentActiveWindow(a1)
	move.l	ib_ActiveScreen(a6),CurrentActiveScreen(a1)
	bsr	FreeAllSemaphores
.NoSemaphore:

;If screen is blanked, re-blank (actually just move the blanking screen to the
;front)
	btst	#STB_SBlanked,Status(a1)
	beq	.SkipReBlank
	move.l	ib_FirstScreen(a6),d0
	cmp.l	BlankScreenPtr(a1),d0
	beq	.SkipReBlank
	bsr	BlankScreen
.SkipReBlank:

;Re-blank mouse if a new window has been activated.
	move.l	MBlankWindow(a1),d0
	beq	.Loop
	cmp.l	ib_ActiveWindow(a6),d0
	beq	.EndMBW
	lea	BlankMouse(pc),a2
	bsr	DoFunc
.EndMBW:

.Loop:	cmp.b	#IECLASS_TIMER,ie_Class(a0)
	beq	.Timer

	cmp.b	#IECLASS_RAWKEY,ie_Class(a0)
	bne	.NotKey
	tst.b	ie_Code+1(a0)
	bmi	.Next	;ignore up key codes

	bsr	RestoreScreen

;Handle immediate blank feature
	tst.l	ImmBlankOption(a1)
	beq	.SkipImmBlank
;Check for lamiga+ramiga
	cmp.b	#IEQUALIFIER_LCOMMAND+IEQUALIFIER_RCOMMAND,ie_Qualifier+1(a0)
	bne	.SkipImmBlank
	bsr	BlankScreen
.SkipImmBlank:

;Perform SunKey
	tst.l	SunKeyOption(a1)
	beq	.SkipSKey
	lea	ActivateWindow(pc),a2
	bsr	DoFunc
.SkipSKey:

;Do Northgate keyboard mapping.
	tst.l	NorthGateOption(a1)
	beq	.SkipNorthgate
	lea	NorthgateTable(pc),a2
	move.w	ie_Code(a0),d0
	bclr	#7,d0
.NorthgateLoop:
	move.b	(a2),d1
	beq	.EndNorthgate
	addq.l	#3,a2
	cmp.b	d0,d1
	bne	.NorthgateLoop
	and.w	#$80,ie_Code(a0)
	move.b	-2(a2),d0
	or.b	d0,ie_Code+1(a0)
	move.b	-1(a2),d0
	bmi	.EndNorthgate
	bset	d0,ie_Qualifier+1(a0)
.EndNorthgate:
.SkipNorthgate:

	btst	#IEQUALIFIERB_LCOMMAND,ie_Qualifier+1(a0)
	bne	.DoLAmiga

;Blank mouse pointer
	tst.l	MBlankOption(a1)
	beq	.NoMBlank
	lea	BlankMouse(pc),a2
	bsr	DoFunc
.NoMBlank:
	bra	.Next

.DoLAmiga:
	tst.l	CmdOption(a1)
	beq	.Next
	cmp.w	#$45,ie_Code(a0)	;ESC?
	bne	.Next
	clr.b	ie_Class(a0)
	lea	PopCLI(pc),a2
	bsr	DoFunc
	bra	.End

.NotKey:	bsr	RestoreScreen
.Timer:	move.w	ib_MouseX(a6),d0
	move.w	ib_MouseY(a6),d1
	cmp.b	#IECLASS_RAWMOUSE,ie_Class(a0)
	beq	.DoMouse
	cmp.w	CurrentX(a1),d0
	bne	.DoMouse
	cmp.w	CurrentY(a1),d1
	beq	.Next

.DoMouse:
	move.w	d0,CurrentX(a1)
	move.w	d1,CurrentY(a1)
	tst.l	MBlankOption(a1)
	beq	.NoRestore
	move.l	ie_TimeStamp(a0),MouseTime(a1)
	lea	RestoreMouse(pc),a2
	bsr	DoFunc
.NoRestore:

;Perform SunMouse
	tst.l	SunMouseOption(a1)
	beq	.SkipSunMouse
	move.w	ie_Qualifier(a0),d0
	and.w	#IEQUALIFIER_LEFTBUTTON+IEQUALIFIER_RBUTTON+IEQUALIFIER_MIDBUTTON,d0
	bne	.SkipSunMouse
	cmp.w	#IECODE_LBUTTON+IECODE_UP_PREFIX,ie_Code(a0)
	beq	.SkipSunMouse
	move.l	SunMouseDelayOption(a1),a2
	move.l	a2,d0
	beq	.SkipLoad
	move.l	(a2),d0
.SkipLoad:
	addq.l	#1,d0
	move.l	d0,SunMouseDelay(a1)
.SkipSunMouse:

;Perform click-to-back.
	cmp.w	#IECODE_RBUTTON,ie_Code(a0)
	bne	.NoCTB
	move.w	ie_Qualifier(a0),d0
	btst	#IEQUALIFIERB_LEFTBUTTON,d0
	beq	.DoSunRMB
	tst.l	STBOption(a1)
	bne	.DoCTB
	tst.l	CTBOption(a1)
	beq	.NoCTB
.DoCTB:	lea	CTB(pc),a2
	bsr	DoFunc
	clr.b	ie_Class(a0)
	bra	.Next

.DoSunRMB:

;Perform SunRMB
	tst.l	SunRMBOption(a1)
	beq	.SkipSunRMB
	lea	ActivateWindow(pc),a2
	bsr	DoFunc
.SkipSunRMB:

.NoCTB:

;Perform click-to-front.
	tst.l	CTFOption(a1)
	beq	.NoCTF
	cmp.w	#IECODE_LBUTTON,ie_Code(a0)
	bne	.NoCTF
	btst	#IEQUALIFIERB_LCOMMAND,ie_Qualifier+1(a0)
	bne	.NoCTF	;to avoid interferring with Snap
	lea	CTF(pc),a2
	bsr	DoFunc
.NoCTF:

;Perform acceleration.
	tst.l	AccelOption(a1)
	beq	.Next

	move.l	ThreshOption(a1),d0
	beq	.T1
	move.l	d0,a2
	move.l	(a2),d0
.T1:	move.w	ie_X(a0),d1
	bpl	.PosX
	neg.w	d1
.PosX:	cmp.w	d0,d1
	bls	.SkipX	;below threshold
	move.l	AccelOption(a1),a2
	move.l	(a2),d1
	muls.w	ie_X(a0),d1
	tst.w	ie_X(a0)
	bpl	.SubDampX
	add.w	DampingConstant(a1),d1
	bra	.DampX
.SubDampX:
	sub.w	DampingConstant(a1),d1
.DampX:	move.w	d1,ie_X(a0)
.SkipX:	move.w	ie_Y(a0),d1
	bpl	.PosY
	neg.w	d1
.PosY:	cmp.w	d0,d1
	bls	.EndAccel
	move.l	AccelOption(a1),a2
	move.l	(a2),d1
	muls.w	ie_Y(a0),d1
	tst.w	ie_Y(a0)
	bpl	.SubDampY
	add.w	DampingConstant(a1),d1
	bra	.DampY
.SubDampY:
	sub.w	DampingConstant(a1),d1

.DampY:	move.w	d1,ie_Y(a0)
.EndAccel:

.Next:	move.l	a0,d1
	move.l	(a0),d0
	move.l	d0,a0
	bne	.Loop

;Check for time-outs (mouse and screen blanking)
	move.l	d1,a0
	tst.l	MBlankOption(a1)
	beq	.NoMTime
	tst.l	MBlankWindow(a1)
	bne	.NoMTime
	move.l	MBlankOption(a1),a0
	move.l	(a0),d0	;get value in seconds
	move.l	d1,a0
	move.l	ie_TimeStamp(a0),d1
	tst.l	MouseTime(a1)
	beq	.InitMBlank
	sub.l	MouseTime(a1),d1
	cmp.l	d0,d1	;TimePassed ? UserSetting
	blo	.NoMTime
	lea	BlankMouse(pc),a2
	bsr	DoFunc
.NoMTime:
	tst.l	SBlankOption(a1)
	beq	.End
	btst	#STB_SBlanked,Status(a1)
	bne	.End
	push	a0
	move.l	SBlankOption(a1),a0
	move.l	(a0),d0	;get value in seconds
	pop	a0
	move.l	ie_TimeStamp(a0),d1
	tst.l	ScreenTime(a1)
	beq	.InitSBlank
	sub.l	ScreenTime(a1),d1
	cmp.l	d0,d1	;TimePassed ? UserSetting
	blo	.End
	bsr	BlankScreen

.End:	bsr	FreeIBase
	move.l	(sp)+,d0
	movem.l	(sp)+,d2/a2/a6
	rts
.InitMBlank:
	move.l	ie_TimeStamp(a0),MouseTime(a1)
	bra	.NoMTime
.InitSBlank:
	move.l	ie_TimeStamp(a0),ScreenTime(a1)
	bra	.End

;Enter with global pointer in A1.
;d0, d1, a0, a1, a5, and a6 are scratch.
;Must always end with a "moveq #0,d0" to set the Z flag.

VBlankRoutine:
	tst.l	SunMouseDelay(a1)
	beq	.End
	subq.l	#1,SunMouseDelay(a1)
	bne	.End
	move.l	a2,-(sp)
	lea	ActivateWindow(pc),a2
	bsr	DoFunc
	move.l	(sp)+,a2
.End:	moveq	#0,d0
	rts

GlobalPtr:
	dc.l	0

NorthgateTable:
;Assign PageUp/PageDown/Home/End to shift-cursor sequences
	dc.b	$6b,$4f,IEQUALIFIERB_LSHIFT
	dc.b	$6c,$4e,IEQUALIFIERB_LSHIFT
	dc.b	$6d,$4c,IEQUALIFIERB_LSHIFT
	dc.b	$6e,$4d,IEQUALIFIERB_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	'QMouse process',0
InputName:
	dc.b	'input.device',0
TrackName:
	dc.b	'trackdisk.device',0
	even

;*************************** Debugging stuff *************************

	IFNE	INFO_LEVEL
KPutFmt:	movem.l	a2/a6,-(sp)
	move.l	4,a6
	lea	KPutChar(pc),a2
	SYS	RawDoFmt
	movem.l	(sp)+,a2/a6
	rts

KPutChar:

;Serial
	comment |
	move.l	a6,-(sp)
	move.l	4,a6
	SYS	RawPutChar
	move.l	(sp)+,a6
	rts
|

;Printer
	comment |
	move.b	#$ff,$bfe301
.Print:	btst	#0,$bfd000
	bne	.Print
	move.b	d0,$bfe101
	rts
|

;Memory
	movem.l	a0-a1,-(sp)
	lea	MemPtr(pc),a1
	move.l	(a1),a0
	move.b	d0,(a0)+
	move.l	a0,(a1)
	movem.l	(sp)+,a0-a1
	rts
MemPtr:	dc.l	$300000
	ENDC

EndAllocCode:	;end of code copied into allocated memory
;***********************************************************************

NewProcTags:
	dc.l	NP_Entry
EntryPoint:
	dc.l	0
	dc.l	NP_Name
ProcNamePtr:
	dc.l	0
	dc.l	NP_Cli,-1
	dc.l	NP_Priority,TaskPriority
	dc.l	0	;end of tags

Template:	dc.b	'M=MBLANK/K/N,'
	dc.b	'S=SBLANK/K/N,'
	dc.b	'N=NOFLICKER/S,'
	dc.b	'CMD/K,'
	dc.b	'A=ACCELERATION/K/N,'
	dc.b	'T=THRESHOLD/K/N,'
	dc.b	'CTB=CLICKTOBACK/S,'
	dc.b	'STB=SCREENTOBACK/S,'
	dc.b	'CTF=CLICKTOFRONT/K/N,'
	dc.b	'SUNMOUSE/S,'
	dc.b	'SUNKEY/S,'
	dc.b	'NORTHGATE/S,'
	dc.b	'NOBEEP/S,'
	dc.b	'NOCLICK/K/N,'
	dc.b	'VERIFY/S,'
	dc.b	'NOVERIFY/S,'
	dc.b	'STAR/S,'
	dc.b	'NoWBenchToFront/S,'
	dc.b	'PUBCHANGE/S,'
	dc.b	'SunMouseDelay/K/N,'
	dc.b	'ImmBlank/S,'
	dc.b	'NoActivate/S,'
	dc.b	'SunRMB/S,'
	dc.b	'QUIT/S',0

IntName:	dc.b	'intuition.library',0
GraphName:
	dc.b	'graphics.library',0
DosName:	dc.b	'dos.library',0
LayersName:
	dc.b	'layers.library',0
UtilityName:
	dc.b	'utility.library',0
BadArgs:	dc.b	'Bad arguments.',$a,0

	dc.b	'$VER: QMouse 2.90 (4.1.94)',$a,0

;The ordering here is special: data registers first, then address
;registers, so that MOVEM may be used to save/restore the data.

	STRUCTURE	pt,0
	LONG	pt_Height
	LONG	pt_Width
	LONG	pt_XOffset
	LONG	pt_YOffset
	LONG	pt_Window
	LONG	pt_Pointer
	BYTE	TagVersion	;special boolean indicator
	BYTE	pt_pad
	LABEL	pt_Sizeof

ResidentCodeSize	equ	EndAllocCode-StartAllocCode

	STRUCTURE	Data,0
	WORD	Nothing	;to make zero special

	LABEL	Sigs
	LONG	QuitSig
	LONG	FuncSig
	LABEL	EndSigs

	LABEL	Semaphores
	STRUCT	MySemaphore,SS_SIZE
	STRUCT	CloseWindowSemaphore,SS_SIZE
	STRUCT	CloseScreenSemaphore,SS_SIZE
	STRUCT	CloseWBSemaphore,SS_SIZE
	LABEL	EndSemaphores

	LONG	Task
	LONG	InputMsgPort
	LONG	InputIORequest
	STRUCT	InputInterrupt,IS_SIZE
	LONG	IntBase
	LONG	GraphBase
	LONG	LayersBase
	LONG	_UtilityBase
	STRUCT	ScreenList,SCREENLISTSIZE*8
	STRUCT	PointerList,POINTERLISTSIZE*pt_Sizeof
	STRUCT	TaskQueue,MAXQUEUE*4+4
	LONG	OldCloseScreen
	LONG	OldCloseWorkBench
	LONG	OldCloseWindow
	LONG	OldSetPointer
	LONG	OldSetWindowPointerA
	LONG	OldClearPointer
	LONG	OldDisplayBeep
	LONG	OldOpenWindowTagList
	LONG	DosBase
	LONG	ArgPtr
	STRUCT	ResidentCode,ResidentCodeSize
	LONG	MouseTime	;timeout for mouse blanking
	LONG	ScreenTime	;for screen blanking
	LONG	BlankScreenPtr
	LONG	BlankScreenWindowPtr
	LONG	MBlankWindow
	WORD	DampingConstant
	LONG	ClickWindow
	STRUCT	ClickTime,TV_SIZE
	LONG	ClickCount
	LONG	Path
	LONG	ChipData
	LONG	RealBackground
	LONG	OriginalCopInit
	WORD	CurrentX
	WORD	CurrentY
	LONG	WaitLineAddress
	LONG	CurrentTopScreen
	LONG	CurrentActiveScreen
	LONG	CurrentActiveWindow
	LONG	SunMouseDelay
	STRUCT	VBlankServer,IS_SIZE
	LONG	IBaseLock

	LABEL	Options
	LONG	MBlankOption
	LONG	SBlankOption
	LONG	FlickerOption
	LONG	CmdOption
	LONG	AccelOption
	LONG	ThreshOption
	LONG	CTBOption
	LONG	STBOption
	LONG	CTFOption
	LONG	SunMouseOption
	LONG	SunKeyOption
	LONG	NorthGateOption
	LONG	BeepOption
	LONG	NoClickOption
	LONG	VerifyOption
	LONG	NoVerifyOption
	LONG	StarOption
	LONG	NoWBenchToFrontOption
	LONG	PubChangeOption
	LONG	SunMouseDelayOption
	LONG	ImmBlankOption
	LONG	NoActivateOption
	LONG	SunRMBOption
	LONG	QuitOption

	BYTE	HandlerInstalled
	BYTE	Status	;various status bits (defined below)
	LABEL	Data_Sizeof

;Bit definitions for Status
	BITDEF	ST,SBlanked,0
	BITDEF	ST,IntInstalled,1

NumSigs	equ	(EndSigs-Sigs)/4
NumSemaphores	equ	(EndSemaphores-Semaphores)/SS_SIZE

	STRUCTURE	CData,0
	STRUCT	ZeroMouse,12
	STRUCT	Copper,MaxCopperSize+4*4
	LABEL	CData_Sizeof

	end
