*
*	SNOOPGLUE.S						:ts=8
*
*	Assembly support routines for snoopdos.c
*
*	This file contains routines to patch/unpatch dos library to point
*	to a C routine, and to call the old routines.
*
*	Does some nasty tricks to patch into dos.library. However, since we
*	checks to see if we are running under 2.0 (with dos.library looking
*	just like any other library), Snoopdos works fine under 2.0.
*
	INCLUDE "exec/types.i"
	INCLUDE "exec/libraries.i"
	INCLUDE	"libraries/dosextens.i"

	XDEF	_installdospatch
	XDEF	_uninstalldospatch
	XDEF	_CallLock
	XDEF	_CallOpen
	XDEF	_CallLoadSeg
	XDEF	_CallExecute
	XDEF	_CallCurrentDir
	XDEF	_CallDeleteFile

	XREF	_DOSBase

	XREF	_NewOpen
	XREF	_NewLock
	XREF	_NewLoadSeg
	XREF	_NewExecute
	XREF	_NewCurrentDir
	XREF	_NewDeleteFile

	XREF	_LVOOpen
	XREF	_LVOLock
	XREF	_LVOLoadSeg
	XREF	_LVOExecute
	XREF	_LVOCurrentDir
	XREF	_LVODeleteFile

CALL	MACRO
	XREF	_LVO\1
	JSR	_LVO\1(A6)
	ENDM

	SECTION	SnoopDos,CODE

*
*	Modify DOS library to call our functions for Open() and Lock();
*	We can't just use SetFunction() because DOS library is non-standard.
*	Instead of using JMP $123456 type instructions, it uses (mostly)
*	MOVEQ #funcnum,D0; BRA.L Dispatch. So, we replace this entire
*	sequence with a standard JMP instruction, steal the number from
*	the MOVEQ instruction and use it when we want to call the real
*	DOS library later on.
*
_installdospatch:
	MOVEM.L	A0-A3/A6,-(A7)		* Save regs
	MOVE.L	4,A6			* Get ExecBase
	MOVE.L	_DOSBase,A0		* Get DOSbase
	BSET	#LIBB_CHANGED,LIB_FLAGS(A0) * Indicate changing DOS library

SetFunc	MACRO
	MOVEM.W	_LVO\1(A0),A1-A3	* Read in current value for func
	MOVEM.W	A1-A3,\1Save		* Save the appropriate data
	MOVEM.W CallOur\1,A1-A3		* Read in replacement code
	MOVEM.W	A1-A3,_LVO\1(A0)	* And modify library
	ENDM

	SetFunc	Open
	SetFunc Lock
	SetFunc LoadSeg
	SetFunc Execute
	SetFunc CurrentDir
	SetFunc DeleteFile

	MOVE.L	A0,A1			* Finally,
	CALL	SumLibrary		* Recalculate library checksum
	MOVEM.L	(A7)+,A0-A3/A6		* Save regs
	RTS

*
*	Reinstall previous DOS patch. If someone has already altered the
*	function, we can't actually exit immediately so return a FALSE
*	result. While there is a very small window which could cause
*	problems (if someone is in the middle of a SetFunction() when
*	this is called), but we'll ignore this mostly.
*
_uninstalldospatch:
	MOVEM.L	A0-A3/A6,-(A7)		* Save regs
	MOVE.L	4,A6			* Get sysbase
	MOVE.L	_DOSBase,A0		* Get DOSBase

TestFunc MACRO
	MOVE.L	CallOur\1+2,D0		* Get old address of function
	CMP.L	_LVO\1+2(A0),D0		* See if changed
	BNE	changed			* If it has, exit with no action
	ENDM

	TestFunc Open
	TestFunc Lock
	TestFunc LoadSeg
	TestFunc Execute
	TestFunc CurrentDir
	TestFunc DeleteFile

	BSET	#LIBB_CHANGED,LIB_FLAGS(A0) * Indicate changing DOS library

RestFunc MACRO
	MOVEM.W	\1Save,A1-A3		* Read in prev. value of vec.
	MOVEM.W	A1-A3,_LVO\1(A0)	* Atomically restore it
	ENDM

	RestFunc Open
	RestFunc Lock
	RestFunc LoadSeg
	RestFunc Execute
	RestFunc CurrentDir
	RestFunc DeleteFile

	MOVE.L	A0,A1			* Finally,
	CALL	SumLibrary		* Update library checksum
	MOVEQ	#1,D0			* Indicate success
	BRA.S	unexit			* And exit

changed:
	MOVEQ	#0,D0			* Indicate failure to uninstall
unexit:
	MOVEM.L	(A7)+,A0-A3/A6		* Restore regs
	RTS				* And exit with return in D0

*
*	This code is the code that gets moved directly into the DOS
*	library vectors
*
CallOurOpen:		JMP	NewOpen
CallOurLock:		JMP	NewLock
CallOurLoadSeg:		JMP	NewLoadSeg
CallOurExecute:		JMP	NewExecute
CallOurCurrentDir:	JMP	NewCurrentDir
CallOurDeleteFile:	JMP	NewDeleteFile

*
*	These are the replacement DOS routines.
*
NewFunc	MACRO
	MOVEM.L	A2-A6/D2-D7,-(A7)	* Save all registers (to be safe)
	JSR	_New\1			* Call C version with params in D1-D3
	MOVEM.L	(A7)+,A2-A6/D2-D7	* Restore registers
	MOVE.L	D0,D1			* Copy return value into D1
*					* [some routines expect this :-( ]
	RTS				* Return to caller, exit value in D0
	ENDM

NewOpen:	NewFunc Open
NewLock:	NewFunc Lock
NewLoadSeg:	NewFunc LoadSeg
NewExecute:	NewFunc Execute
NewCurrentDir:	NewFunc CurrentDir
NewDeleteFile:	NewFunc	DeleteFile

*
*	Allow's C to call the old DOS functions. If we're running Kikstart 2.0
*	then just call the original function directly. Otherwise, calculate
*	where to go from the code stored in the vector.
*
OldDosCall MACRO
	LEA.L	\1Save,A0	* Get pointer to func save vector
	MOVE.W	#_LVO\1,D0	* Get library offset vector
	BRA.S	CallDos		* Skip to execute it
	ENDM

_CallOpen:		OldDosCall Open
_CallLock:		OldDosCall Lock
_CallLoadSeg:		OldDosCall LoadSeg
_CallExecute:		OldDosCall Execute
_CallCurrentDir:	OldDosCall CurrentDir
_CallDeleteFile:	OldDosCall DeleteFile

	NOP				* Stop Lattice messing up final branch
CallDos:
	MOVEM.L	D1-D3/D6/D7/A6,-(A7)	* Save registers
	MOVE.W	(A0),D6			* Get previous instruction
	CMP.W	#$4ef9,D6		* Is it a JMP instruction?
	BNE	dos_exec		* If not, then call using special code
	MOVE.L	2(A0),A0		* Else it's 2.0 or SetFunction()
	MOVE.L	_DOSBase,A6		* Put DOSBase into A6 as expected
	JSR	(A0)			* Call original function
	BRA.S	callexit		* And exit

dos_exec:
	MOVE.W	(A0),D6			* Get MOVEQ instruction
	EXT.W	D6			* Convert data to word
	EXT.L	D6			* and then longword
	EXG.L	D0,D6			* Swap with library vector offset
	MOVE.W	4(A0),D7		* Get offset to DOS routine from LVO
	MOVE.L	_DOSBase,A6		* Get DOSBase 
	ADD.W	D6,D7			* Calculate offset value
	JSR	4(A6,D7.W)		* Call DOS function dispatcher

callexit:
	MOVEM.L	(A7)+,D1-D3/D6/D7/A6	* Restore registers
	RTS				* And return to C caller

	SECTION SnoopData,DATA

OpenSave	DS.W	3
LockSave	DS.W	3
LoadSegSave	DS.W	3
ExecuteSave	DS.W	3
CurrentDirSave	DS.W	3
DeleteFileSave	DS.W	3

	END
