*
*  Switch.a  -	 Copyright 1990 D.W.Reisig
*
*   #	 date	 by    Comment
*  -- --------- ----- ---------------------
*   0 29-Oct-89	 DWR   Created for Tracksalve
*   1 10-Apr-90	 DWR   TrackSalve 1.3, second pc catch 
*

*		nolist
		INCLUDE "Call.i"
		INCLUDE "ts.i"
		INCLUDE "exec/tasks.i"
		INCLUDE "exec/resident.i"
*		list



		CSECT	text,code,0,1,2			Any xref's after this are 16-bit reloc

*********************************************************
*
*  PCtoTS
*
*  We need to catch Trackdisk in its highest task-loop.	 Otherwise we could lose control
*  after an rts.  This loop is rather small:  7 instuctions.  One of the instructions is a
*  jsr _LVOWait(a6). Trackdisk is waiting here for a timer or an IORequest and will be here
*  most of the time.  If we change the return address to our code, Trackdisk smoothly enters
*  our code.  We change this address only if TD is at this point.
*
*  Input:	a0	UnitData structure
*		a4	Globals
*
*  Output:	d0	0 if caught else E_CATCHTD
*
*  Usage:	d2	PC of TD-task at the point where we want to change it
*		d6	Timeout counter
*		d7	Error
*
*		a2	TSControl structure
*		a3	UnitData structure
*		a5	Task stucture of Trackdisk unit task
*
*  NB!	We are accessing the TSControl structure in a way that needs protection.
*

		XREF	_PCSubOffset,_PCWaitOffset
WaitRtnPC	equ	-20				Offset PC to TC_SPUPPER during a Wait()
SubRtnPC	equ	-16				Offset PC to TC_SPUPPER during a subcall

		Func	PCtoTS,@PCtoTS
		movem.l d2-d4/d6-d7/a2-a3/a5,-(a7)

		moveq.l #0,d7				No error for now
		move.l	a0,a3				UnitData structure

		move.l	UD_TDReq+IO_UNIT(a3),a5		Lea Unit structure of the Trackdisk IORequest
		move.b	TDU_UnitNr(a5),d4		Get unit number (to set InUse flag later)
		move.l	MP_SIGTASK(a5),a5		Lea Task control block associated with this port
		move.l	UD_TSControl(a3),a2		Control structure
		move.l	TSC_TDTag(a2),d2		Begin of TD code
		move.l	d2,d3
		add.l	#$1582,d2			Add offset for PC in subroutine
		add.l	#$1592,d3			Add offset for PC when waiting
		moveq.l #25,d6				We put a limit here, so we will not hang forever if TD is dead

SearchPC	ExCall	Forbid				Prevent TD to become active
		move.l	TC_SPUPPER(a5),a0		Initial stackpointer of TD
		cmp.l	SubRtnPC(a0),d2			Is TD in its single taskloop subroutine?
		beq.s	ChangePCSub			Yes, change the return address..
		cmp.l	WaitRtnPC(a0),d3		Is TD Waiting()?
		beq.s	ChangePCWait			Yes, change the return address..
		LibCall Permit				No, give TD a chance to change its situation
		moveq.l #10,d1				We will wait for a fifth of a second
		DOSCall Delay				Sleeping
		dbra	d6,SearchPC			And try again..
		moveq.l #E_CATCHTD,d7			But not forever, so set error
		bra.s	PCtoTSRtn			Quit..

ChangePCSub	move.l	_PCSubOffset(a4),d0		PC found in the subroutine, get matching offset in TrackSalve
		add.l	TSC_TSCode(a2),d0		Add begin of TrackSalve code
		move.l	d0,SubRtnPC(a0)			Change return addres to adress in our code
		bra.s	PCChanged
ChangePCWait	move.l	_PCWaitOffset(a4),d0		PC found waiting, get matching offset in TrackSalve
		add.l	TSC_TSCode(a2),d0		Add begin of TrackSalve code
		move.l	d0,WaitRtnPC(a0)		Change return addres to adress in our code
PCChanged
		move.l	d3,UD_ReturnTD(a3)		Save TD return address (we use the Wait() call to leave TS)
		move.l	TC_Userdata(a5),UD_Userdata(a3) Keep original value (although TD does not use it)
		move.l	a3,TC_Userdata(a5)		Make UnitData structure known to TD-task
		bset.b	d4,TSC_InUse(a2)		Set flag to prevent freeing the TSControl structure
		LibCall Permit				Multitasking on again

PCtoTSRtn	move.l	d7,d0				Error
		movem.l (a7)+,d2-d4/d6/d7/a2-a3/a5
		rts



*********************************************************
*
*  RelocateTS
*
*  Modify position dependend values in copied TSCode
*
*  Input:	a0	TSControl structure
*

		Func	RelocateTS,@RelocateTS
		move.l	TSC_TSCode(a0),a0
		lea.l	TSCodeRelocTable(pc),a1
10$		move.w	(a1)+,d0
		beq.s	20$
		move.l	a0,d1
		add.l	0(a0,d0.w),d1
		move.l	d1,0(a0,d0.w)
		bra.s	10$
20$		rts

		xref	TSCodeRelocTable


*********************************************************
*
*  RelocateTD
*
*  Modify position dependend values in relocated Trackdisk code versions 33.127 and 34.1
*
*  Input:	a0	TSControl structure
*

		ifd	__stdargs
		Func	_RelocateTD
		move.l	4(a7),a0
		endc

		Func	RelocateTD,@RelocateTD
		move.l	TSC_TSTag(a0),a1		Begin of copy of Trackdisk code
		move.l	TSC_TDTag(a0),d0		Begin of Trackdisk code (in ROM)
*		cmp.l	a1,RT_MATCHTAG(a1)		Does our copy has its self-pointer right?
*		beq.s	RelocateRtn			Yes, already relocated..
		sub.l	a1,d0				TD org minus TD copy = relocation value
		lea.l	RelocTable(pc),a0		Table of relative addresses to relocate
		bra.s	Relocate20			Enter loop..
Relocate10	sub.l	d0,0(a1,d1.w)			Only Amiga makes it possible!
Relocate20	move.w	(a0)+,d1			Get offset in TD-code
		bne.s	Relocate10
RelocateRtn	rts



*--	You can obtain this table by comparing version 1.2 and 1.3 and do some editing on the result

RelocTable	dc.w	$0002
		dc.w	$0006
		dc.w	$000E
		dc.w	$0012
		dc.w	$0016
		dc.w	$0324
		dc.w	$032A
		dc.w	$03C0
		dc.w	$091C
		dc.w	$0940
		dc.w	$0966
		dc.w	$0980
		dc.w	$0994
		dc.w	$09B6
		dc.w	$09C4
		dc.w	$09CC
		dc.w	$0A00
		dc.w	$0A04
		dc.w	$0A08
		dc.w	$0A0C
		dc.w	$0A10
		dc.w	$0A14
		dc.w	$0A1C
		dc.w	$0A20
		dc.w	$0A24
		dc.w	$0A28
		dc.w	$0A2C
		dc.w	$0A30
		dc.w	$0A34
		dc.w	$0A38
		dc.w	$0A3C
		dc.w	$0A40
		dc.w	$0A44
		dc.w	$0A48
		dc.w	$0A4C
		dc.w	$0A50
		dc.w	$0A54
		dc.w	$0A58
		dc.w	$0A5C
		dc.w	$0A60
		dc.w	$0A64
		dc.w	$0A68
		dc.w	$0A6C
		dc.w	$0A70
		dc.w	$127E
		dc.w	$12EA
		dc.w	$1316
		dc.w	$1352
		dc.w	$1386
		dc.w	$13E4
		dc.w	$1446
		dc.w	$1690
		dc.w	$16B8
		dc.w	$195A
		dc.w	$19A2
		dc.w	$19B2
		dc.w	0



*********************************************************
*
*  PatchMem
*
*  Change memory according to a table
*
*  Input:	a0	Memory to change
*		a1	Patchtable  (Offset.w, Size.w, Patch.w[Size])
*

		Func	PatchMem,@PatchMem
		movem.l a2,-(a7)
		move.l	a0,a2				Lea begin of memory

GetNextPatch	move.l	a2,a0				Begin of memory
		move.w	(a1)+,d0			Get offset in code to put patch
		add.w	d0,a0				Lea destination of patch
		move.w	(a1)+,d1			Get size of patch in words
		beq.s	PatchMemRtn			No patch, done..
		bra.s	CopyPatch10			Enter copyloop
CopyPatch	move.w	(a1)+,(a0)+			Copy word of code
CopyPatch10	dbra	d1,CopyPatch			Loop ..
		bra.s	GetNextPatch			Examine next entry in Patchtable

PatchMemRtn	movem.l (a7)+,a2
		rts


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

		END

