
;*******************************************************
;
;	SCSI Driver 'Shutdown' filter.
;
;	Written by Matt Gulick.		Started August 9,1988
;
;	Copyright Apple Computer, Inc. 1988,89
;
;*******************************************************

;*******************************************************
;
;	This file contains the 'Shutdown' filter as defined
;	in the ERS.
;
;*******************************************************

;*******************************************************
;
;	Revision History:
;
;*******************************************************
;
;	Aug 9,		1988	File started.
;	Mar	8,		1989	Modified to support Warm/Cold
;						Shutdown.

				STRING		PASCAL
				BLANKS		OFF
				PAGESIZE	70
				PRINT		NOGEN
				PRINT		NOMDIR
				MACHINE		M65816

				IMPORT		direct_page
				IMPORT		default_dib
				IMPORT		tot_dib_cnt
				IMPORT		active_starts
				IMPORT		stop_unit

				PRINT		OFF

				INCLUDE		'scsihd.equates'
				INCLUDE		'M16.MEMORY'
				INCLUDE		'M16.UTIL'
				PRINT		ON

				EJECT
			
;*******************************************************
;
;	Main Entry point to the 'Shutdown' filter.  This
;	"Filter" is called when a Device is no longer
;	desired in the tables.  The following code segment
;	first checks to see if the DIB being shut down is
;	the default dib (it will only be shutdown durring
;	startup).  If it is the default dib we will do
;	nothing and exit with no error.  If on the other
;	hand it is a valid DIB, we will clear some key
;	fields within the dib and then we will alter the
;	links to skip this DIB, then we update a field in the
;	first DIB of this memory segment.  This count tells
;	how many active dibs there are in that segment.  If
;	this reaches zero we will then dispose this handle
;	because it is unused.  If we later need space to
;	bring new devices online and we don't have the space,
;	we'll get it at that time.  If on exit there are
;	still any active dibs that use this code segment then
;	a DRVR_BUSY error will be returned.  If no DIBs are
;	active then no error will be returned.
;
;	Inputs:		[dib_ptr]	=	Last DIB built		(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	DRVR_BUSY
;				Carry		=	1
;						unless we have no more dibs then
;				Acc			=	0
;				Carry		=	0
;
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	GS/OS Direct Page
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************

				EXPORT	shutdown
shutdown		PROC
											;
											; Check to see if this is for the
											; 'default_dib'.  If it is, then we
											; need to exit with a DRVR_BUSY Error.
											;
				lda		<dib_ptr
				cmp		#default_dib
				bne		@real_dib
				lda		<dib_ptr+2
				cmp		#^default_dib
				bne		@real_dib
											;
											; Return a DRVR BUSY error and take
											; no action.
											;
				lda		#drvr_busy
				sec
				rts

@real_dib

;-------------------------------------------------------------------------------

				IF		warm_ss_suprt = true		THEN

											;
											; It's a real DIB.  Is it a COLD
											; SHUTDOWN?
											;
				lda		>warm_cold_flag
				and		#$0001
				beq		@do_cold			;It's Cold
											;
											; Yeah, so it's a warm shutdown.  But
											; does this device know what that
											; means?  We'll see!
											;
				ldy		#dib.dvcchar
				lda		[dib_ptr],y
				and		#restartable
				beq		@do_cold			;Not restartable.
											;
											; Mark as OFFLINE and Relaxing
											;
				ldy		#dib.dvcflag
				lda		[dib_ptr],y
				and		#dvc_online--\
						$ffff
				ora		#relaxing
				sta		[dib_ptr],y
											;
											; Is the device removable?
											;
;				ldy		#dib.dvcchar
;				lda		[dib_ptr],y
;				and		#removable
;				beq		@continue			;Not removable.
											;
											; Reinitialise DIB Device Number to
											; zero.
											;
				lda		#null
				ldy		#dib.devnum
				sta		[dib_ptr],y
											;
											; Do the Head and Forward links also.
											;
				ldy		#dib.headlnk
				sta		[dib_ptr],y

				ldy		#dib.fdvclnk
				sta		[dib_ptr],y
											;
											; Get out of here updating only
											; the active DIB count not the
											; total DIB Count.
											;
@continue		jmp		@update_acnt

 				ENDIF

;-------------------------------------------------------------------------------

											;
											; It's a real DIB.  Issue a $1B
											; START/STOP Command.  Ignor all
											; errors, not all devices support
											; this optional command.
											;
@do_cold		jsr		stop_unit
											;
											; Set Device to Default.
											;
				ldy		#dib.dvcflag
				lda		#wait_mode++\
						cold_dib
				sta		[dib_ptr],y
											;
											; Shut it down.  To do this we clear
											; the DIB Device Number.
											;
				ldy		#dib.devnum
				lda		[dib_ptr],y
				sta		@this_device		;Save for a while
				lda		#null
				sta		[dib_ptr],y
											;
											; Dec Device count for this memory
											; segment.
											;
				ldy		#dib.handle			;Set up to deref the handle to find
				lda		[dib_ptr],y			;where this memory segment starts.
				sta		<scsi_zp0
				sta		|@deref_handle		;Save in case we dispose of it.
				ldy		#dib.handle+2
				lda		[dib_ptr],y
				sta		<scsi_zp0+2
				sta		|@deref_handle+2
											;
											; Deref the Handle.
											;
				ldy		#$0002
				lda		[scsi_zp0]
				tax
				lda		[scsi_zp0],y
				sta		scsi_zp0+2
				stx		scsi_zp0
											;
											; Dec the count.  If now = zero then
											; dispose of this handle.
											;
				ldy		#dib.mem_dib_cnt
				lda		[scsi_zp0],y
				dec		a
				sta		[scsi_zp0],y

				bne		@chk_links			;Not zero.  Update link if it exists.
											;
											; We have emptied this memory segment.
											; We now need to deallocate this
											; memory segment.
											;
				ldy		#$0002
				pushword |@deref_handle+2
				pushword |@deref_handle
				_disposehandle					
											;
											; Find location of previous DIB in
											; linked list.
											;
											; Start at the default DIB.
											;
				lda		#default_dib
				sta		<prev_dib
				lda		#^default_dib
				sta		<prev_dib+2
											;
											; Does this DIB point to the
											; requseted DIB?
											;
				ldy		#dib.linkptr+2

@prev_loop		lda		[prev_dib],y
				tax							;Save value for later

				lda		[prev_dib]			;Do they match?
				cmp		<dib_ptr
				bne		@advnc_loop			;No.
				cpx		<dib_ptr+2
				beq		@set_prev			;Yes. Set new links.

@advnc_loop		stx		<prev_dib+2			;No.  Advance to the next DIB.
				sta		<prev_dib
				ora		<prev_dib+2
				bne		@prev_loop
				bra		@chk_links
											;
											; Set previous DIBs link pointer
											; to the DIB that follws the one
											; being shutdown.
											;
@set_prev		lda		[dib_ptr]
				sta		[prev_dib]
				lda		[dib_ptr],y
				sta		[prev_dib],y
											;
											; We must now check to see if this DIB
											; that was shutdown is linked to any
											; other DIBs.
											;
@chk_links		ldy		#dib.dvcchar
				lda		[dib_ptr],y
				and		#linked_dvc
				beq		@update_tcnt		;No it is not linked.
											;
											; It is linked. First we need to go to
											; the head device if this is not it. If
											; it is the head, then we need to do
											; something completly different.
											;
				clc
				ldy		#dib.headptr
				lda		[dib_ptr],y
				ldy		#dib.headptr+2
				ora		[dib_ptr],y
				beq		@at_head

				lda		[dib_ptr],y
				sta		<scsi_zp0+2
				ldy		#dib.headptr
				lda		[dib_ptr],y
				sta		<scsi_zp0
				bra		@chk_lnk_loop

@at_head		lda		<dib_ptr
				sta		<scsi_zp0
				lda		<dib_ptr+2
				sta		<scsi_zp0+2
				
@chk_lnk_loop	ldy		#dib.fdvclnk
				lda		[scsi_zp0],y
				beq		@update_tcnt

				cmp		@this_device
				beq		@update_link

				ldy		#dib.fdvcptr
				lda		[scsi_zp0],y
				tax
				ldy		#dib.fdvcptr+2
				lda		[scsi_zp0],y
				sta		<scsi_zp0+2
				stx		<scsi_zp0
				bra		@chk_lnk_loop

@update_link	ldy		#dib.headptr
				lda		[dib_ptr],y
				sta		[scsi_zp0],y
				ldy		#dib.headptr+2
				lda		[dib_ptr],y
				sta		[scsi_zp0],y

				ldy		#dib.fdvcptr
				lda		[dib_ptr],y
				sta		[scsi_zp0],y
				ldy		#dib.fdvcptr+2
				lda		[dib_ptr],y
				sta		[scsi_zp0],y

				ldy		#dib.fdvclnk
				lda		[dib_ptr],y
				sta		[scsi_zp0],y
											;
											; Clear the FDVCLNK and HEADLNKs
											; incase this segment is used later.
											;
				lda		#null
				sta		[dib_ptr],y
				ldy		#dib.headlnk
				sta		[dib_ptr],y
											;
											; Update the total DIB Count.  If
											; there are still some dibs left,
											; then we will return a DRVR_BUSY
											; error.  If all done we will return
											; no error.  This will cause us to
											; be purged.
											;
@update_tcnt	dec		|tot_dib_cnt
@update_acnt	dec		|active_starts
				beq		@thats_all_folks
											;
											; More left. Return an error.
											;
				lda		#DRVR_BUSY
				sec
				rts
											;
											; ThThThThThaaaaattt'ss all folks.
											; I give, go ahead purge me.  I
											; LOVE IT when you do that to
											; me!!!!!!
											;
@thats_all_folks
											;
											; Exit no Error.
											;
				lda		#null
				clc
				rts
											;
											; Internal Data Areas.
											;
@deref_handle	dc.l	null
@this_device	dc.w	null

				ENDP

				END

				EJECT
