;	PatchZeus.asm - resident module to patch PPI Zeus SCSI driver
;
;	Written by Michael L. Hitch
;		Montana State University
;		osymh@msu.oscs.montana.edu
;

	include	"exec/types.i"
	include	"exec/nodes.i"
	include	"exec/memory.i"
	include	"exec/resident.i"
	include	"exec/execbase.i"
	include	"exec/libraries.i"
	include	"exec/io.i"
	include	"exec/errors.i"
	include	"devices/scsidisk.i"
	include	"libraries/configvars.i"
	include	"libraries/expansion.i"
	include	"hardware/cia.i"
	include	"hardware/custom.i"

;	xref	_LVOOpenLibrary
;	xref	_LVOCloseLibrary
;	xref	_LVOFindConfigDev
	xref	_LVOAllocSignal
	xref	_LVOFreeSignal
	xref	_LVOFindTask
	xref	_LVOFindName
	xref	_LVOSignal
	xref	_LVOWait
	xref	_LVOAllocMem
	xref	_LVOSumKickData
	xref	_LVOForbid
	xref	_LVOPermit
	xref	_LVODisable
	xref	_LVOEnable
	xref	_LVOSupervisor
	xref	_LVOCacheControl
	xref	_ciaa
	xref	_custom

	csect	text
;
; Program entry point
;
	movem.l	d2-d3/a2/a5/a6,-(sp)
	move.l	4,a6
;
; *** process command line arguments
;
	jsr	AddResident(pc)		; Add program to KickMemPtr list
;*** check status?
;
	movem.l	(sp)+,d2-d3/a2/a5/a6
	rts
;
;
AddResident
	move.l	KickMemPtr(a6),a0
1$:
	move.l	a0,d0
	beq.s	NewResident		; Not found
	move.l	LN_NAME(a0),a1		; name of this entry
	lea	TagName(pc),a2		; my name
2$:
	move.b	(a1)+,d0
	cmp.b	(a2)+,d0
	bne.s	3$
	tst.b	d0
	bne	2$
; found entry
	moveq	#1,d0
	rts
3$:
	move.l	(a0),a0			; Next entry
	bra	1$
;
; Layout of memory block
;	ds.b	8		Overhead space for AllocAbs
;	ds.b	ML_SIZE		Memory entry list
;	ds.b	ME_SIZE		Memory entry
;	ds.b	8		RomTab pointer array
;	ds.b	nnn		Resident module
;
KM_RTPTR equ	ML_SIZE+ME_SIZE		; Location of RomTag pointer array
KM_SIZE	equ	KM_RTPTR+8		; Size of KickMem info header
;
NewResident:
	moveq	#KM_SIZE+8,d0		; size of KickMem header + 8 byte overhead
	add.l	RomTag+6(pc),d0
	sub.l	RomTag+2(pc),d0		; add length of resident module
	move.l	#MEMF_24BITDMA!MEMF_CLEAR,d1
	jsr	_LVOAllocMem(a6)	; allocate memory
	tst.l	d0
	bne.s	1$
	moveq	#KM_SIZE+8,d0		; size of KickMem header + 8 byte overhead
	add.l	RomTag+6(pc),d0
	sub.l	RomTag+2(pc),d0		; add length of resident module
	move.l	#MEMF_CHIP!MEMF_CLEAR,d1
	jsr	_LVOAllocMem(a6)	; allocate memory
	tst.l	d0
	bne.s	1$
	moveq	#2,d0
	rts
1$:
	move.l	d0,a0			; a0 = address of allocated memory
	lea	8(a0),a2		; a2 = address of MemList
	move.b	#NT_MEMORY,LN_TYPE(a2)	; initialize MemList
	move.w	#1,ML_NUMENTRIES(a2)
	move.l	a0,ML_ME(a2)
	moveq	#KM_SIZE+8,d0		; size of KickMem header + 8 byte overhead
	move.l	d0,ML_ME+ME_LENGTH(a2)
	move.l	RomTag+6(pc),d0
	sub.l	RomTag+2(pc),d0		; d0 = length of resident module
	add.l	d0,ML_ME+ME_LENGTH(a2)
	lea	KM_SIZE+8(a0),a0	; point to RomTag structure
	lea	RomTag(pc),a1
	move.l	a0,d1
	sub.l	a1,d1			; relocation offset for RomTag addresses
	move.l	a0,KM_RTPTR(a2)		; RomTag Pointer
	subq.l	#1,d0
2$:	move.b	(a1)+,(a0)+		; copy resident module
	dbra	d0,2$
	move.l	KM_RTPTR(a2),a0		; Point to RomTag structure
	add.l	d1,RT_MATCHTAG(a0)	; relocate absolute addresses
	add.l	d1,RT_ENDSKIP(a0)
	add.l	d1,RT_NAME(a0)
	add.l	d1,RT_IDSTRING(a0)
	add.l	d1,RT_INIT(a0)
	move.l	RT_NAME(a0),LN_NAME(a2)	; set MemList name same as RomTag name
;*** add to KickMemPtr & KickTagPtr
	jsr	_LVOForbid(a6)
	lea	KickMemPtr(a6),a0
3$:	move.l	(a0),d0
	beq.s	4$
	move.l	d0,a0
	bra	3$
4$:	tst.l	KickMemPtr(a6)
	beq.s	5$
	move.l	a0,LN_PRED(a2)		; is this really needed?
	move.l	a2,(a0)
	bra.s	6$
5$:	move.l	a2,KickMemPtr(a6)
6$:	move.l	KickTagPtr(a6),d0
	beq.s	7$
	bset	#31,d0
	move.l	d0,KM_RTPTR+4(a2)	; link to existing RomTag pointer array
7$:	lea	KM_RTPTR(a2),a0		; address of our RomTag pointer array
	move.l	a0,KickTagPtr(a6)
	jsr	_LVOSumKickData(a6)
	move.l	d0,KickCheckSum(a6)
	jsr	_LVOPermit(a6)
;*** call resident initialization routine?
	lea	myClearCache(pc),a5	; clear caches before executing
	jsr	_LVOSupervisor(a6)	;    the initialization code
	move.l	KM_RTPTR(a2),a0
	move.l	RT_INIT(a0),a0
	jsr	(a0)			; Call initialization routine
	moveq	#0,d0
	rts
;
myClearCache:
	cpusha	bc			; push and invalid both caches
	rte
;
;
; The resident RomTag structure
;
RomTag:	dc.w	RTC_MATCHWORD
	dc.l	RomTag
	dc.l	EndModule
	dc.b	RTF_COLDSTART
	dc.b	37
	dc.b	NT_KICKMEM
	dc.b	0
	dc.l	TagName
	dc.l	TagID
	dc.l	TagInit

TagName: dc.b	"PatchZeus",0
	dc.b	"$VER: "
TagID:	dc.b	"PatchZeus 1.1 (26.10.93)",$0d,$0a,0
ZeusName: dc.b	"PPSscsi2.device",0
ExpName: EXPANSIONNAME
	ds.w	0

PATCHLOC1 equ	$19b0			; setup prior to starting 53C710 script
PATCHLOC2 equ	$1a38			; wait for 53C710 completion
PATCHLOC3 equ	$1f88			; Interrupt - signal completion
PATCHLOC4 equ	$1ff8			; Interrupt - phase mismatch
PATCHLOC5 equ	$20ee			; 53C170 setup - set SCNTL0
PATCHLOC6 equ	$210c			; 53C710 setup - set SIEN
PATCHLOC7 equ	$1d32			; Interrupt - received interrupt
PATCHLOC8 equ	$28ae			; SCSIcmd - adjust scsi_Actual
PATCHLOC9 equ	$28a0			; SCSIcmd - adjust scsi_SenseActual
PATCHLOC10 equ	$0ef0			; Compare partition name with BSTR name
PATCHLOC11 equ	$1ec0			; Interrupt - code $ff01
PATCHLOC12 equ	$136c			; Device Open - return HFERR_NoBoard
PATCHLOC13 equ	$27fc			; ScsiCmd - return HFERR_BadStatus
PATCHLOC14 equ	$1b5a			; Sync request period

;
; Entry when resident module is initialized
;
TagInit:
	movem.l	d2/d3/a2/a4,-(sp)
	lea	_custom,a0			; Address of custom chip
	move.w	potinp(a0),d0			; Read register
	ori.w	#$0c00,d0			; Set output, data = 1
	move.w	d0,potgo(a0)
	move.w	potinp(a0),d0			; Read data
	andi.w	#$0400,d0			; Test if Mouse right button pressed
	beq.s	SkipMouse			; Yes, don't check left button
	lea	_ciaa,a0
	btst	#6,ciapra(a0)			; Test if Mouse left button pressed
	beq.s	TagExit				; Yes, skip it
SkipMouse:
	btst	#AFB_68040,AttnFlags+1(a6)	; is it 68040?
	bne.s	Is_68040
	cmp.w	#36,LIB_VERSION(a6)		; if version 2.0 or later
	bge.s	TagExit				; then AFB_68040 is valid
; *** 68040 not detected on WB1.3
	moveq	#AFF_68030!AFF_68020,d0
	and.w	AttnFlags(a6),d0		; Kludge for WB1.3
	bne.s	Is_68040
TagExit:
	moveq	#0,d0
	movem.l	(sp)+,d2/d3/a2/a4
	rts

Is_68040:
	lea	DeviceList(a6),a0		; get address of device list
	lea	ZeusName(pc),a1			; Device driver name
	jsr	_LVOFindName(a6)		; Find device driver
	tst.l	d0
	beq	TagExit				; Didn't find it
	move.l	d0,a2
;
; Found device driver;  make sure it's the correct version and validate
;	the places we are going to patch
;
	bra.s	debug1			; ** DEBUG ** nop to enable color changes
	move.w	#$00ff,$00dff180	; ** DEBUG ** set screen color = cyan
	move.l	#$00800000,d0
delay1:	subq.l	#1,d0
	bgt	delay1
	move.w	#$0888,$00dff180
debug1:
	cmp.w	#98,LIB_REVISION(a2)		; Check if revision 98
	bne	TagExit				; Nope, skip it
	move.l	LN_NAME(a2),a4
	lea	-$521(a4),a4			; Base relative to start of ROM
	cmp.l	#$584f206d,PATCHLOC1(a4)	; verify patch points
	bne	TagExit
	cmp.l	#$206d0008,PATCHLOC2(a4)
	bne	TagExit
	cmp.l	#$217c0000,PATCHLOC3(a4)
	bne	TagExit
	cmp.l	#$02800000,PATCHLOC4(a4)
	bne	TagExit
	cmp.l	#$117c00cc,PATCHLOC5(a4)
	bne	TagExit
	cmp.l	#$10bc00bf,PATCHLOC6(a4)
	bne	TagExit
	cmp.l	#$02800000,PATCHLOC7(a4)
	bne	TagExit
	cmp.l	#$23680004,PATCHLOC8(a4)
	bne	TagExit
	cmp.l	#$3368001a,PATCHLOC9(a4)
	bne	TagExit
	cmp.l	#$e5802b40,PATCHLOC10(a4)
	bne	TagExit
	cmp.l	#$20280030,PATCHLOC11(a4)
	bne	TagExit
	cmp.l	#$0cad0000,PATCHLOC12(a4)
	bne	TagExit
	cmp.l	#$2b7c0000,PATCHLOC13(a4)
	bne	TagExit
	cmp.l	#$001e0104,PATCHLOC14(a4)
	bne	TagExit
	bra.s	debug2			; ** DEBUG ** nop to enable color changes
	move.w	#$0f0f,$00dff180	; ** DEBUG ** set screen color = magenta
	move.l	#$00800000,d0
delay2:	subq.l	#1,d0
	bgt	delay2
	move.w	#$0888,$00dff180
debug2:
;
; install patches
;
; relocate jumps back into the driver
;
	move.l	a4,d0
	lea	Patch1end(pc),a0
	move.l	(a0),-(a0)
	add.l	d0,(a0)
	lea	Patch2end(pc),a0
	move.l	(a0),-(a0)
	add.l	d0,(a0)
	lea	Patch3end(pc),a0
	move.l	(a0),-(a0)
	add.l	d0,(a0)
	lea	Patch4end(pc),a0
	move.l	(a0),-(a0)
	add.l	d0,(a0)
	lea	Patch7end(pc),a0
	move.l	(a0),-(a0)
	add.l	d0,(a0)
	lea	Patch8end(pc),a0
	move.l	(a0),-(a0)
	add.l	d0,(a0)
	lea	Patch9end(pc),a0
	move.l	(a0),-(a0)
	add.l	d0,(a0)
	lea	Patch10end(pc),a0
	move.l	(a0),-(a0)
	add.l	d0,(a0)
	lea	Patch11end(pc),a0
	move.l	(a0),-(a0)
	add.l	d0,(a0)
	lea	Patch11end2(pc),a0
	move.l	(a0),-(a0)
	add.l	d0,(a0)
	lea	Patch12end(pc),a0
	move.l	(a0),-(a0)
	add.l	d0,(a0)
	lea	Patch12end2(pc),a0
	move.l	(a0),-(a0)
	add.l	d0,(a0)
;
; patch new code into the driver
;
	lea	Patch1(pc),a0		; setup prior to starting 53C710 script
	move.w	#$4ef9,PATCHLOC1(a4)
	move.l	a0,PATCHLOC1+2(a4)
	lea	Patch2(pc),a0
	move.w	#$4ef9,PATCHLOC2(a4)	; wait for 53C710 completion
	move.l	a0,PATCHLOC2+2(a4)
	lea	Patch3(pc),a0
	move.w	#$4ef9,PATCHLOC3(a4)	; Interrupt - signal waiting task
	move.l	a0,PATCHLOC3+2(a4)
	lea	Patch4(pc),a0
	move.w	#$4ef9,PATCHLOC4(a4)	; Interrupt - phase mismatch
	move.l	a0,PATCHLOC4+2(a4)
;	move.w	#$00c4,PATCHLOC5+2(a4)		; don't check parity
;	move.w	#$00be,PATCHLOC6+2(a4)		; don't interrupt on parity
;	move.b	#$c4,$e94003
;	move.b	#$be,$e94000
	lea	Patch7(pc),a0		; Interrupt - save 52C710 info for debug
	move.w	#$4ef9,PATCHLOC7(a4)
	move.l	a0,PATCHLOC7+2(a4)
	lea	Patch8(pc),a0		; SCSIcmd - adjust scsi_Actual
	move.w	#$4ef9,PATCHLOC8(a4)
	move.l	a0,PATCHLOC8+2(a4)
	lea	Patch9(pc),a0		; SCSIcmd - adjust scsi_SenseActual
	move.w	#$4ef9,PATCHLOC9(a4)
	move.l	a0,PATCHLOC9+2(a4)
	lea	Patch10(pc),a0		; Compare partition name with BSTR name
	move.w	#$4ef9,PATCHLOC10(a4)
	move.l	a0,PATCHLOC10+2(a4)
	lea	Patch11(pc),a0		; Interrupt - code $ff01
	move.w	#$4ef9,PATCHLOC11(a4)
	move.l	a0,PATCHLOC11+2(a4)
	lea	Patch12(pc),a0		; Return HFERR_NoBoard on device open
	move.w	#$4ef9,PATCHLOC12(a4)
	move.l	a0,PATCHLOC12+2(a4)
	move.w	#HFERR_BadStatus,PATCHLOC13+4(a4) ; SCSIcmd - return HFERR_BadSTatus
	move.w	#100/4,PATCHLOC14(a4)		; negotiate for 100usec period
	lea	510(a2),a0			; Initialize unused command
	lea	Cmd_Invalid(pc),a1		; entries in dispatch vector
	moveq	#31,d0
1$:	tst.l	(a0)
	bne.s	2$				; this entry defined
	move.l	a1,(a0)
2$:	addq.l	#4,a0
	dbra	d0,1$
; to do:  patch new command routines
	move.w	#9850,LIB_REVISION(a2)		; Change revision to 985x
	moveq	#1,d0
	movem.l	(sp)+,d2/d3/a2/a4
	rts

;
; Prior to starting 53C710 script execution, initialize task pointer and
;    allocate a signal
; Also, turn off data cache if buffer does not start and stop on a quadword
;    boundary
;
Patch1:
	addq.w	#4,sp			; restore stack
	move.l	-8(a5),a0		; Unit pointer
	move.l	468+20(a0),d0		;  data length
	add.l	468+24(a0),d0		;  + data start -> buffer end
	or.l	468+24(a0),d0		;  | data start
	and.l	#15,d0			; Flag if buffer not quadword boundary
	move.l	d0,-(sp)		; save flag
	move.l	d0,-(sp)		; save old cache settings
	move.l	a6,-(sp)
	move.l	4,a6
	tst.l	d0			; test flag
	beq.s	1$			; buffer starts and stops on quadword boundary
	moveq	#0,d0			; Turn off data cache
	move.l	#CACRF_EnableD,d1
	jsr	_LVOCacheControl(a6)
	move.l	d0,4(sp)		; save previous cache settings
1$:
	sub.l	a1,a1
	jsr	_LVOFindTask(a6)	; get our task pointer
	move.l	8(a5),a0		; device pointer
	move.l	d0,92(a0)		; save task pointer
	moveq	#-1,d0
	jsr	_LVOAllocSignal(a6)	; allocate available signal
	move.l	(sp)+,a6
	move.l	8(a5),a0
	move.l	d0,96(a0)		; save signal number
	clr.l	100(a0)			; clear DBC
	move.l	12(a5),a1		; ***** Debug: get IOR pointer
	move.l	IO_UNIT(a1),34(a0)	; ***** Debug: save last unit pointer
	jmp	$800019b6
Patch1end:
	dc.l	$000019b6

;
; Wait for signal from interrupt server and free signal
;
Patch2:
	move.l	8(a5),a0
	move.l	96(a0),d1		; get signal number
	moveq	#1,d0
	lsl.l	d1,d0			; form mask
	move.l	a6,-(sp)
	move.l	4,a6
	jsr	_LVOWait(a6)		; Wait for signal
	move.l	8(a5),a0
	move.l	96(a0),d0
	jsr	_LVOFreeSignal(a6)	; free the signal
	move.l	(sp)+,a6
	move.l	(sp)+,d0		; get previous cache settings
	move.l	(sp)+,d1		; get flag
	beq.s	1$			; if data cache was turned off,
	move.l	#CACRF_EnableD,d1
	move.l	a6,-(sp)
	move.l	4,a6
	jsr	_LVOCacheControl(a6)	; restore previous cache settings
	move.l	(sp)+,a6
1$:
	jmp	$80001a46
Patch2end:
	dc.l	$00001a46

;
; When 53C710 has completed, signal waiting task (if any)
;
Patch3:
	move.l	#1,84(a0)		; set flag [redundant now]
	move.l	92(a0),d0
	beq.s	1$			; skip if no task pointer
	move.l	d0,a1
	move.l	96(a0),d1
	moveq	#1,d0
	lsl.l	d1,d0			; form signal number
	move.l	a6,-(sp)
	move.l	4,a6
	jsr	_LVOSignal(a6)		; signal the waiting task
	move.l	(sp)+,a6
1$:
	jmp	$80001f90
Patch3end:
	dc.l	$00001f90

;
; Phase mismatch, store SBCL register in circular buffer
; Also, if phase == STATUS, save DBC for adjustment of data transfer length
;
Patch4:
	move.l	8(a5),a0		; Device pointer
	moveq	#0,d1
	move.b	43(a0),d1		; buffer index
	move.b	d0,44(a0,d1)		; store SBCL value
	addq.w	#1,d1			; increment index
	andi.w	#$000f,d1		; wrap around
	move.b	d1,43(a0)
	moveq	#7,d1
	and.l	d1,d0			; mask phase bits
	cmpi.b	#3,d0			; is phase STATUS?
	bne.s	1$
;	move.l	104(a0),100(a0)		; isolate DBC
	move.l	$e94024,100(a0)		; get dcmd/dbc */
	clr.b	100(a0)
1$:
	jmp	$80002088
Patch4end:
	dc.l	$00002088

;
;  Debug stuff - save DSTAT & SSTAT0 in circular buffer on interrupt
;		 also save DCMD/DBC and DSP
;
Patch7:
	andi.l	#$ff,d0			; replaced instruction
	move.b	d0,-14(a5)		; store DSTAT
	move.l	-4(a5),a0		; load Device pointer
	moveq	#0,d1
	move.b	42(a0),d1		; buffer index
	move.b	d0,60(a0,d1)		; store DSTAT in buffer
	move.b	-19(a5),61(a0,d1)	; store SSTAT0 in buffer
	addq.w	#2,d1
	andi.w	#15,d1
	move.b	d1,42(a0)		; update buffer index
	move.l	$e94024,104(a0)		; save DCMD/DBC
	move.l	$e9402c,108(a0)		; save DSP
	jmp	$80001d3c
Patch7end:
	dc.l	$00001d3c

;
; SCSI_cmd - set scsi_Actual correctly
;
Patch8:
	move.l	4(a0),8(a1)		; scsi_Actual = scsi_Length
	move.l	-12(a5),a0		; Unit pointer
	move.l	2066(a0),a0		; Device pointer
	move.l	100(a0),d0		; get DBC
	sub.l	d0,8(a1)		; adjust scsi_Actual
	jmp	$800028b4
Patch8end:
	dc.l	$000028b4

;
; SCSI_cmd - set scsi_Sense_Actual correctly
;
Patch9:
	move.w	26(a1),28(a0)		; scsi_SenseActual = scsi_SenseLength
	move.l	-12(a5),a1		; Unit pointer
	move.l	2066(a1),a1		; Device pointer
	move.l	100(a1),d0		; get DBC
	sub.w	d0,28(a0)		; adjust scsi_Actual
	move.l	4(a0),100(a1)		; set DBC = scsi_Length so scsi_Actual
	jmp	$800028a6		;    will get set to zero
Patch9end:
	dc.l	$000028a6

;
; Compare RDB name to Device Node BSTR name
;
Patch10:
	asl.l	#2,d0			; convert BSTR pointer to address
	addq.l	#1,d0			; skip count byte
	move.l	d0,-16(a5)
	jmp	$80000ef6
Patch10end:
	dc.l	$00000ef6

;
; Interrupt code $ff01 - Phase not MsgOut after selection
;
Patch11:
	move.l	48(a0),d0		; load DSPS
	cmpi.l	#$0000ff01,d0		; is it Phase not MsgOut after selection?
	beq.s	Patch11b		; yes, check current phase
Patch11a:
	jmp	$80001f0c		; continue
Patch11end:
	dc.l	$00001f0c
Patch11b:
	move.b	8(a0),d1		; get SBCL
	andi.b	#$07,d1			; mask off phase
	cmpi.b	#$02,d1			; is it Command Out?
	bne.s	Patch11a		; no, continue
;	andi.b	#$f7,4(a0)		; <reset ATN?>
	lea	-32602(a4),a1		; restart script processing for command out
	move.l	a1,44(a0)		; DSPS
	clr.b	-18(a5)			; clear flag to signal completion
	jmp	$80001f24		; (we aren't done yet)
Patch11end2:
	dc.l	$00001f24

;
; Device Open - return HFERR_NoBoard if unit number >= 100
;
Patch12:
	move.l	-16(a5),d0		; get unit number
	cmpi.l	#8,d0
	bhs.s	Patch12a		; it's not valid
	jmp	$80001386
Patch12end:
	dc.l	$00001386
Patch12a:
	moveq	#HFERR_NoBoard,d1	; return error
	cmpi.l	#100,d0
	bhs.s	Patch12b
	moveq	#IOERR_OPENFAIL,d1
Patch12b:
	move.l	-8(a5),a0
	move.b	d1,IO_ERROR(a0)
	jmp	$80001450
Patch12end2:
	dc.l	$00001450

;
; Invalid commands - return error code
;
Cmd_Invalid:
	move.l	4(sp),a0		; Get IOR pointer
	moveq	#IOERR_NOCMD,d0		; set error code
	move.b	d0,IO_ERROR(a0)
	clr.l	IO_ACTUAL(a0)		; clear io_Actual
	rts

EndModule:

	end
