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

		section	main,code

***********************************************************************************
;--------------	this is the patch-function that replaces any original LIBRARY code.
;--------------	Note that we have a little recursion-protector here that will
;--------------	prevents recursions that occur inside the original vector 
;--------------	just aslong as the PatchCode() is active
;--------------	

;--------------	NOTE: the device patch follows below & is a LOT more complicated


		xdef	Patch_START
Patch_START	dc.b	PATCH_IDSTRING	; sphead_Ident
		dc.l	0		; sphead_Owner
		dc.l	0		; sphead_OriginalFunction

		xdef	PatchInfoStruct
PatchInfoStruct	dc.l	0		; sphead_Info
		push	a0
		lea	(recursionWord,pc),a0
		tst.w	(a0)
		bne.b	OriginalCode
		move.w	#-1,(a0)
		pop	a0
		addq.l	#1,(ActiveCalls)
		push	a6
		bsr.b	SetupReplyMsg1
		push	a6
		movea.l	(4,sp),a6
		xdef	OriginalVector0
OriginalVector0	jsr	$ffff0000
		pop	a6
		bsr	SendReplyMsg1
		lea	(recursionWord,pc),a6
		clr.w	(a6)
		pop	a6
		subq.l	#1,(ActiveCalls)
		rts

OriginalCode	pop	a0	; from initial push at line #ß
		xdef	OriginalVector1
OriginalVector1	jsr	$ffff000
		rts

;--------------	HEY DUDES: I know this is a bit messy, but I couldn't think of
;--------------	anything better for my purpose. Of course, Snoopy will probably
;--------------	miss out one or two calls in "stress situations", but
;--------------	"better safe than sorry".....
recursionWord	dc.w	0	; if bit15 is 0, then no call is currently active 

***********************************************************************************
;--------------	sets up the replymsg given by the patch function
;--------------	
;--------------	=> d0-d7/a0-a5: Registers before function call
;--------------	<= a6: PatchCallMessage structure
;--------------	allocate data structure
SetupReplyMsg1	pushm	d0-d7/a0-a5
		movea.l	(execBase).w,a6
		move.l	(thistask),d0
		cmp.l	(ThisTask,a6),d0
		beq	.SKIPTHIS
		move.l	#smsg_SIZEOF,d0
		move.l	#MEMF_PUBLIC|MEMF_CLEAR,d1
		CALL	AllocMem,<(execBase).w>
		move.l	d0,a6
		popm	d0-d7/a0-a5

;--------------	fail if NULL; else save all registers
		cmpa.l	#0,a6
		beq	.FAIL
		SAVEREGS smsg_RegsBeforeCall
.FAIL		rts
.SKIPTHIS	suba.l	a6,a6
		popm	d0-d7/a0-a5
		rts

***********************************************************************************
;--------------	sends the reply message back
;--------------	
;--------------	=> d0-d7/a0-a5: Registers after function call
;--------------	
;--------------	fail if NULL; else save all registers
SendReplyMsg1	cmpa.l	#0,a6
		beq	.FAIL
		SAVEREGS smsg_RegsAfterCall
		pushm	d0-d7/a0-a5

		movea.l	(execBase).w,a0
		move.l	(ThisTask,a0),(smsg_Task,a6)
		move.l	(PatchInfoStruct,pc),(smsg_Info,a6)
		movea.l	a6,a5			; a5 = Data structure
		movea.l	a0,a6

;--------------	setup other message stuff
		move.b	#NT_MESSAGE,(LN_TYPE,a5)
		move.w	#smsg_SIZEOF,(MN_LENGTH,a5)
		move.w	#MSGTYPEF_LIBRARY,(smsg_Type,a5) ; **DO NOT REMOVE**
		move.l	#0,(MN_REPLYPORT,a5)
		move.l	(myport),a0		; port
		move.l	a5,a1			; message
		CALL	PutMsg

;--------------	new: handle timeout
		push	a6
		movea.l	(PatchInfoStruct,pc),a1
		move.l	(spatch_Timeout,a1),d1
		beq.b	.NOTIMEOUT
		cmpi.l	#-1,d1
		beq.b	.BREAKOUT
		BRBS.B	#31,d1,.NOTIMEOUT	; skip negative timeout values
		CALL	Delay,<(dosBase)>
		bra.b	.NOTIMEOUT
.BREAKOUT	move.l	#SIGBREAKF_CTRL_C,d0
		CALL	Wait,<(execBase).w>
.NOTIMEOUT	pop	a6

		popm	d0-d7/a0-a5
.FAIL		rts

		xdef	Patch_SIZEOF
Patch_SIZEOF	dc.l	*-Patch_START

;--------------	Now the two patches for BeginIO()/AbortIO(); Both patches
;--------------	are very small and "minimalistic" (I had a much more complicated
;--------------	one in the beginning but it didn't work -> this one does ;-)

		xdef	BeginIO_Patch
BeginIO_Patch:	dc.b	PATCH_IDSTRING	; sphead_Ident
		dc.l	0		; sphead_Owner
		dc.l	0		; sphead_OriginalFunction
		dc.l	0		; sphead_Info (size of memory)
		dc.l	0		; sphead_DevicePtr

;--------------	"here we go again with a funky intro": BeginIO gets the
;--------------	IOBlock in a1 and a pointer to the device in a6.
		addq.l	#1,(ActiveCalls)
		push	d4
		move.l	(4,sp),d4
		pushm	d0-d3/d5-d7/a0-a6
		lea	(BeginIO_Patch,pc),a5
		move.w	#MSGTYPEF_DEVICE|MSGTYPEF_BEGINIO,d5
		jsr	PerformDevicePatch
		popm	d0-d3/d5-d7/a0-a6
		pop	d4
		subq.l	#1,(ActiveCalls)
		xdef	BeginIO_Jump
BeginIO_Jump:	jsr	$ffff0000	; proceed with IO call
		rts
		xdef	BeginIO_SIZEOF
BeginIO_SIZEOF	dc.l	*-BeginIO_Patch

		xdef	AbortIO_Patch
AbortIO_Patch:	dc.b	PATCH_IDSTRING	; sphead_Ident
		dc.l	0		; sphead_Owner
		dc.l	0		; sphead_OriginalFunction
		dc.l	0		; sphead_Info (size of memory)
		dc.l	0		; sphead_DevicePtr
		addq.l	#1,(ActiveCalls)
		push	d4
		move.l	(4,sp),d4
		pushm	d0-d3/d5-d7/a0-a6
		lea	(AbortIO_Patch,pc),a5
		move.w	#MSGTYPEF_DEVICE,d5
		jsr	PerformDevicePatch
		popm	d0-d3/d5-d7/a0-a6
		pop	d4
		subq.l	#1,(ActiveCalls)
		xdef	AbortIO_Jump
AbortIO_Jump:	jsr	$ffff0000	; proceed with IO call
		rts
		xdef	AbortIO_SIZEOF
AbortIO_SIZEOF	dc.l	*-AbortIO_Patch


**************************************************************************
;--------------	Really perform a device patch (because BeginIO()/AbortIO()
;--------------	really are *VERY* similar)
;--------------	
;--------------	=> a5: APTR begin_of_patch
;--------------	   d5: WORD flags
;--------------	
;--------------	Note: This function DOES NOT save any registers, the patches
;--------------	above have to take care of this!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;--------------	
		ENTRY	PerformDevicePatch
		move.l	a1,d6			; d6 = pointer to IO block
		move.l	#sdmsg_SIZEOF,d7	
		add.l	(sphead_Info,a5),d7	; d0 = size of allocated memory
		move.l	d7,d0
		move.l	#MEMF_PUBLIC|MEMF_CLEAR,d1
		CALL	AllocMem,<(execBase).w>
		tst.l	d0
		beq.b	PerformDevicePatch_done
		movea.l	d0,a4
		move.l	d4,(sdmsg_PC,a4)

;--------------	copy IO block
		move.l	(sphead_Info,a5),d0
		subq.w	#1,d0
		lea	(sdmsg_SIZEOF,a4),a1	; target
		movea.l	d6,a0			; source
.COPYMEMORY	move.b	(a0)+,(a1)+
		dbra	d0,.COPYMEMORY

;--------------	setup other message stuff
		move.b	#NT_MESSAGE,(LN_TYPE,a4)
		move.w	d7,(MN_LENGTH,a4)
		move.l	(sphead_DevicePtr,a5),(sdmsg_DeviceBase,a4)
		move.l	(ThisTask,a6),(sdmsg_Task,a4)
		move.w	d5,(sdmsg_Type,a4) ; **DO NOT REMOVE**
		move.l	#0,(MN_REPLYPORT,a4)

;--------------	send message and immediately continue processing
		move.l	(myport),a0		; port
		move.l	a4,a1			; message
		CALL	PutMsg
		DONE	PerformDevicePatch

		section	memory,bss

;--------------	*** DO NOT REMOVE THIS ***, its here just to make sure
;--------------	SLINK doesn't place the BSS section as the second section
;--------------	(DETACHSTARTUPARGS macro (see main.asm) relys on a certain
;--------------	section layout which could be scratched somehow)
		ds.w	1

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