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

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

;*******************************************************
;
;	Revision History:
;
;*******************************************************

;	Aug 13,		1988	File started.

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

				IMPORT		rpm_blk_num
				IMPORT		wpm_blk_num
				IMPORT		stat_cont
				IMPORT		rebuild
				IMPORT		read_pm_blk
				IMPORT		write_pm_blk

				IMPORT		call_type
				IMPORT		internal
				IMPORT		main_drvr
				IMPORT		f_partition
				IMPORT		direct_page
				IMPORT		gsos_dpage
				IMPORT		internal_buff
				IMPORT		set_our_dp
				IMPORT		chk_lnk_offline
				IMPORT		set_disk_sw
				IMPORT		leave_switched	; *** MSG 12/12/91 ***
				IMPORT		disk_switch
				IMPORT		rebld_dibs
				IMPORT		unit_state
				IMPORT		test_unit_rdy
				IMPORT		trash_volume
				IMPORT		trash_it
				IMPORT		ko_cache
				IMPORT		trans_flag
				IMPORT		uses_dc
				IMPORT		dib_data_struct
				IMPORT		display_cnt
				IMPORT		current_fmt
				IMPORT		opt1_blk_cnt
				IMPORT		opt1_blk_siz
				IMPORT		opt1_interleave
				IMPORT		opt1_med_siz
				IMPORT		opt2_blk_cnt
				IMPORT		opt2_blk_siz
				IMPORT		opt2_interleave
				IMPORT		opt2_med_siz
				IMPORT		opt3_blk_cnt
				IMPORT		opt3_blk_siz
				IMPORT		opt3_interleave
				IMPORT		opt3_med_siz
				IMPORT		format_data
				IMPORT		check_532_rw
				IMPORT		sense_data
				IMPORT		t_dvc_blocks
				IMPORT		un_formatted

				ENTRY		reset_dvc
				ENTRY		eject
				ENTRY		s_config_parms
				ENTRY		s_wait_mode
				ENTRY		format_opt
				ENTRY		assign_part
				ENTRY		arm_signal
				ENTRY		disarm_sig
				ENTRY		set_p_map
				ENTRY		format_dvc

				PRINT		OFF

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

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

				IF			scsi_dtype = direct_acc	THEN

				ENTRY		sdp
				ENTRY		svp

				ENDIF

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

				EJECT
			
;*******************************************************
;
;	Main Entry point to the 'Control' filter.  This
;	"Filter" is called when a Control Command comes in.
;	See the headers of the seperate sections for the
;	details of the commands.
;
;	Called via 'JSR'
;
;	Inputs:		[dib_ptr]	=	Target DIB	l	(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Returns via 'RTS'
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;	or
;				Acc			=	Error
;				Carry		=	1
;
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************
			
				EXPORT	control
control			PROC
										;
										; Check to see if this is a set Config call.
										; If so, then don't worry yet about ONLINE
										; or REMOVABLE.
										;
				lda		<cont_code
				cmp		#$0003
				beq		@control
										;
										; Is the device Removable?
										;
				ldy		#dib.dvcchar

				lda		[dib_ptr],y
				and		#removable
				beq		@online			;No.
										;
										; This device is removable.  Now we
										; need to check to see if the unit
										; has gone offline, (then we need to
										; report that to the OS) or if the
										; unit has come back online (Rebuild
										; the DIBs).
										;
				jsr		unit_state
				beq		@online			;**** test Code ****
				bit		|un_formatted	;**** test Code ****

				bpl		@sec_out		;**** test Code ****
;				bmi		@rts_out
										;
										; Check to see if this is a Device Specific
										; Status call.  If so, then don't worry
										; about it being Offline
										;
@online			lda		<cont_code
				bmi		@control
										;
										; Is the device online?
										;
				ldy		#dib.dvcflag

				lda		[dib_ptr],y
				and		#dvc_online
				bne		@control		;Yes.
										;
										; Device is currently offline.
										;
				lda		#drvr_off_line
@sec_out		sec
@rts_out		rts
										;
										; Zero out the Data Chaining Flag
										;
@control		stz		|uses_dc
										;
										; Clear transfer count.
										;
				lda		#null
				ldy		#dib.trx_len
				sta		[dib_ptr],y
				ldy		#dib.trx_len+2
				sta		[dib_ptr],y
										;
										; Entry to the Control Call Filter.
										; This filter acts as a mini driver.
										; It examines the Control Code sent
										; and calls the appropriate routines.
										; If the Command is $8000 or greater,
										; then it will be routed to the device
										; specific section.
										;
				lda		<cont_code
				bmi		@do_dvc_spec	;Device Specific Code.
										;
										; Check the range of the command.
										;
				cmp		#max_c_cmd+1
				bge		@error
										;
										; Convert to an index
										;
				asl		a
				tax
				jsr		(@ncmd_tbl,x)
				rts
										;
										; Normal Command Table.
										;
@ncmd_tbl		dc.w	reset_dvc		;Coded
				dc.w	format_dvc		;Coded
				dc.w	eject			;Coded
				dc.w	s_config_parms	;Coded Temp
				dc.w	s_wait_mode		;Coded
				dc.w	format_opt		;Coded
				dc.w	assign_part
				dc.w	arm_signal		;Coded & Tested
				dc.w	disarm_sig		;Coded & Tested
				dc.w	set_p_map		;Coded & Tested
										;
										; Bad Command Error.
										;
@error			lda		#drvr_bad_req
				sec
				rts
										;
										; Device Specific Code handling
										; starts here.
										;
@do_dvc_spec
;-------------------------------------------------------------------------------

				IF			scsi_dtype = direct_acc	THEN

				cmp		#$F000
				blt		@do_scsi_cmd
				and		#$00ff
				cmp		#max_config_cmd+1
				bge		@error
										;
										; Convert to an index
										;
				asl		a
				tax
				jsr		(@c_cmd_tbl,x)
				rts
										;
										; Config Command Table.
										;
@c_cmd_tbl		dc.w	sdp				;Coded (Set Disk Parms)
				dc.w	svp				;Coded (Set Volume Parms)

@do_scsi_cmd

				ENDIF

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

										;
										; Check version of data structure
										;
				stz		@chk_zero		;Precondition the flag to no check
				lda		[buff_ptr]
				beq		@version_0
				cmp		#$0001
				bne		@error
										;
										; Version 1.  Preserve the data pointer
										; currently used in the DIB and replace
										; it with a pointer to the USER supplied
										; Data Chaining instructions.
										;
				clc
				ldy		#dib.trx_ptr
				lda		[dib_ptr],y
				sta		|dib_data_struct

				lda		<buff_ptr
				adc		#ds.DCcode		;Offset to first D.C. Data
				sta		[dib_ptr],y

				ldy		#dib.trx_ptr+2
				lda		[dib_ptr],y
				sta		|dib_data_struct+2

				lda		<buff_ptr+2
				adc		#^ds.DCcode
				sta		[dib_ptr],y
										;
										; Set flag to indicate that D.C. Commands
										; were used and that we need to restore
										; the pointer in the DIB to the normal
										; data descriptor.  Also set the flag to
										; indicate that we need to check the first
										; buffer pointer for Zero.
										;
				dec		|uses_dc
				dec		@chk_zero
										;
										; Do the rest normally and check the above
										; flag later.
										;
@version_0		clc
				lda		<buff_ptr
				adc		#$0002
				sta		<scsi_mdrvr

				lda		<buff_ptr+2
				adc		#null
				sta		<scsi_mdrvr+2
				
				ldy		#$000C
				lda		[scsi_mdrvr],y
				sta		<buff_ptr
				ldy		#$000C+2
				lda		[scsi_mdrvr],y
				sta		<buff_ptr+2
										;
										; Check First Buffer for $00000000
										;
				ora		<buff_ptr
				bne		@DH_Branch
				lda		@chk_zero
				bne		@error
										;
										; Call Main Driver
										;
@DH_Branch		lda		#scsit_cont
				sta		|call_type
				jsr		|main_drvr
				bcs		@exit
										;
										; Save transfer count.
										;
				ldy		#dib.trx_len
				lda		[dib_ptr],y
				sta		@call_trns_cnt
				ldy		#dib.trx_len+2
				lda		[dib_ptr],y
				sta		@call_trns_cnt+2
										;
										; Check to see if we need to handle
										; a Disk Switched Event.  If not,
										; then exit.
										;
				lda		disk_switch		;= $ffff
				bpl		@exit
										;
										; Don't Trash the Volumes.
										;
				jsr		set_disk_sw		;DISK_SW for each Linked DIB.
				jsr		rebld_dibs		;Rebuild DIBs.
				lda		#null
				clc
										;
										; Restore Direct Page Values.
										;
@exit			pha
				php
										;
										; Check the D.C. Flag
										;
				lda		|uses_dc
				beq		@set_our_zp

				ldy		#dib.trx_ptr
				lda		|dib_data_struct
				sta		[dib_ptr],y

				ldy		#dib.trx_ptr+2
				lda		|dib_data_struct+2
				sta		[dib_ptr],y
				
				stz		|uses_dc		;Reset Flag

@set_our_zp		jsr		set_our_dp
										;
										; Set transfer count.
										;
				lda		@call_trns_cnt
				sta		<trans_cnt
				lda		@call_trns_cnt+2
				sta		<trans_cnt+2

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

				IF			cache_blks = true	THEN

										;
										; If it is a Data Call and has a timer
										; that is adjusted by the block count,
										; then this call could cause the cache
										; to be in-validated.
										;
				lda		|trans_flag
				and		#scsit_data++\	;Is it a data call
						scsic_tout++\	;with a block count
						scsit_cont		;Writing data?
				cmp		#scsit_data++\
						scsic_tout++\
						scsit_cont
				bne		@no_write		;No.
				jsr		ko_cache		;Yes. Kill the blocks.

				ENDIF

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

										;
										; Exit.
										;
@no_write		plp
				pla
				rts
										;
										; Data Area.
										;
@call_trns_cnt	dc.l	null			;Transfer count befor we steped on it.
@chk_zero		dc.w	null			;Check first DCMove Flag.

				ENDP

				EJECT
			
;*******************************************************
;
;	This Control Call is used to reset a particular
;	device to it's default settings.  A Device Driver
;	should configure itself based on the contents of the
;	drivers configuration parameter list.  This call
;	should reset any media variables that were modified
;	through a Set_Format_Options call.
;
;	Called via 'JSR'
;
;	Inputs:		[dib_ptr]	=	Target DIB	l	(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Returns via 'RTS'
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;	or
;				Acc			=	Error
;				Carry		=	1
;
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************
			
				EXPORT	reset_dvc
reset_dvc		PROC
										;
										; Issue a REeZero Unit Call to the device.
										;
										; Set Main Driver Pointer to
										; our data for the command.
										;
				lda		#cmd_$8001
				sta		<scsi_mdrvr
				lda		#^cmd_$8001
				sta		<scsi_mdrvr+2
										;
										; Call Main Driver
										;
				lda		#scsit_cont
				sta		|call_type
				jsr		|main_drvr
										;
										; Update Transfer Count.
										;
				stz		<trans_cnt
				stz		<trans_cnt+2
										;
										; Exit No Error.
										;
				lda		#$0000
				clc
				rts
										;
										; Command Data for this call.
										;
cmd_$8001		dc.b	$01
				dcb.b	11,$00

				ENDP

				EJECT
			
;*******************************************************
;
;	The Format call will ensure that the media is
;	formatted and ready to receive data.  If the DIB
;	points to a partition then the Format call will
;	return no error while taking no action (If it is
;	partitioned, then it must have been formatted or the
;	partition map could not have been written.  Right?).
;
;	Called via 'JSR'
;
;	Inputs:		[dib_ptr]	=	Target DIB	l	(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Returns via 'RTS'
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;	or
;				Acc			=	Error
;				Carry		=	1
;
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************
			
				EXPORT	format_dvc
format_dvc		PROC

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

				IF		character_dvc = true	THEN
										;
										; It's a character device and
										; the Acc is zero by virtue of
										; the missed branch.
										;
				stz		<trans_cnt
				stz		<trans_cnt+2
				lda		#null
				clc
				rts

				ENDIF

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

				IF			scsi_dtype = apple_cd\
				OR			scsi_dtype = changer	THEN
										;
										; If the device is a CD_ROM, then
										; it is write protected.
										;
				stz		<trans_cnt
				stz		<trans_cnt+2
				lda		#drvr_wrt_prot
				sec
				rts

				ENDIF

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

				IF			scsi_dtype = mcd_40	THEN

										;
										; Is the device Write Enabled?
										;
				ldy		#dib.dvcchar
				lda		[dib_ptr],y
				and		#write_allow
				beq		@write_protect
										;
										; Set Format Parm Pointer.
										;
				lda		#@format_p
				sta		<scsi_mdrvr
				lda		#^@format_p
				sta		<scsi_mdrvr+2
										;
										; Set our own buffer pointer
										; for this call.
										;
				lda		#format_data
				sta		<buff_ptr
				lda		#^format_data
				sta		<buff_ptr+2
										;
										; Set our own request count
										; for this call.
										;
				stz		<rqst_cnt
				stz		<rqst_cnt+2
										;
										; It's a Control Call.
										;
				lda		#scsit_cont
				sta		|call_type
										;
										; Set Internal Command Flag
										;
				dec		|internal
										;
										; Issue the Call
										;
				jsr		|main_drvr

				pha
				php

@test_loop		jsr		|test_unit_rdy
				bcc		@fmt_done
				cmp		#$FE08
				beq		@test_loop
				cmp		#$FE02
				bne		@test_loop
				bra		@tape_io_error

@fmt_done		plp
				pla

				rts
										;
										; Return an I/O Error
										;
@tape_io_error	plp
				pla

				lda		#drvr_io
				sec
				rts
										;
										; Return a Write Protected Error
										;
@write_protect	lda		#drvr_wrt_prot
				sec
				rts
										;
										; FORMAT Command Packet
										;
@format_p		dc.b	$04				;Command Number
				dc.b	$00				;Vendor Unique
				dc.b	$00				;Reserved
				dc.b	$00				;SCSI Interleave (MSB)
@interleave		dc.b	interleave		;SCSI Interleave (LSB)
				dcb.b	6,$00			;Reserved

				ENDIF

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

				IF			scsi_dtype = direct_acc	THEN
										;
										; It's a block device.  But is
										; it partitioned?  If it has a
										; zero start block then there is
										; no partition.
										;
				stz		@error			;Set to No Error.
				ldy		#dib.start_blk
				lda		[dib_ptr],y
				ldy		#dib.start_blk+2
				ora		[dib_ptr],y
				beq		@over
										;
										; Well, it is a partition.  But is it
										; the only partition.  By this, I mean,
										; is it a disk with a partition map
										; containing only two entries, one for
										; the partition map and the other for
										; a single volume?
										;
										; First check if this is the first or
										; second entry in the Partition Map.
										;
				ldy		#dib.part_blk
				lda		[dib_ptr],y
				xba
				cmp		#$0003
				bge		@do_part_fmt	;Past the second entry.  Do as if multi
										;partition.
										;
										; Is it linked?  Let's see.
										;
				ldy		#dib.dvcchar
				lda		[dib_ptr],y
				and		#linked_dvc
				beq		@over			;Not linked. Must be only one.

@do_part_fmt	jsr		trash_volume	;Trash this volume only.
				jmp		@clean_exit		;Sorry.  Not the only volume
										;
										; Well there is no getting around
										; it.  We need to issue a format
										; call to this device.
										;
										; Setup from last format options call
										;
@over			lda		|current_fmt
				dec		a
				asl		a
				asl		a
				asl		a
				asl		a
				tax

				ldy		#dib.blksize
				lda		|opt1_blk_siz,x
				sta		[dib_ptr],y
				xba
				sta		@blk_size+2

				lda		|opt1_blk_cnt,x
				sta		|t_dvc_blocks

				lda		|opt1_blk_cnt+2,x
				sta		|t_dvc_blocks+2

				stz		@blk_size
				shortm
				lda		opt1_interleave,x
				sta		@interleave
				longm
										;
										; Preserve the Current Direct
										; Page values stored in the
										; buff_ptr and rqst_cnt fields.
										;
				pei		<buff_ptr
				pei		<buff_ptr+2
				pei		<rqst_cnt
				pei		<rqst_cnt+2
										;
										; Set Format Parm Pointer.
										;
				lda		#@format_p
				sta		<scsi_mdrvr
				lda		#^@format_p
				sta		<scsi_mdrvr+2
										;
										; Set our own buffer pointer
										; for this call.
										;
				lda		#format_data
				sta		<buff_ptr
				lda		#^format_data
				sta		<buff_ptr+2
										;
										; Set our own request count
										; for this call.
										;
				stz		<rqst_cnt
				stz		<rqst_cnt+2
										;
										; It's a Control Call.
										;
				lda		#scsit_cont
				sta		|call_type
										;
										; Set Internal Command Flag
										;
				dec		|internal
										;
										; Issue the Call
										;
				jsr		|main_drvr
				bcc		@rebuild_dib
										;
										; Save error code
										;
				sta		@error
										;
										; Rebuild the DIB.
										;
@rebuild_dib	jsr		rebld_dibs
										;
										; Restore the Direct Page Values.
										;
@return_dp		pla
				sta		<rqst_cnt+2
				pla
				sta		<rqst_cnt
				pla
				sta		<buff_ptr+2
				pla
				sta		<buff_ptr
										;
										; Restore the Acc result.
										;
@clean_exit		jsr		set_disk_sw
				lda		@error
				and		#$7FFF
				cmp		#$7E00
				bge		@io_error
				cmp		#$0001
				rts
										;
										; Some kind of I/O Error.
										;
@io_error		lda		#drvr_io
				rts
										;
										; Data for this call
										;
@error			dc.w	null
										;
										; MODE SELECT Command Packet
										;
@mode_select	dc.b	$15				;Command Number
				dc.b	$00				;Vendor Unique
				dc.b	$00				;Reserved
				dc.b	$00				;Reserved
				dc.b	$00				;Resolved by Main Driver
				dcb.b	6,$00			;Reserved
										;
										; MODE SELECT Data
										;
@mode_data		dc.b	$00				;Command Number
				dc.b	$00				;Reserved
				dc.b	$00				;Reserved
				dc.b	$08				;Length of extension
				dc.b	$00				;Block Count (MSB)
				dc.b	$00				;Block Count
				dc.b	$00				;Block Count
				dc.b	$00				;Block Count (LSB)
@blk_size		dc.b	$00				;Block Size (MSB)
				dc.b	$00				;Block Size
				dc.b	$02				;Block Size
				dc.b	$00				;Block Size (LSB)
										;
										; FORMAT Command Packet
										;
@format_p		dc.b	$04				;Command Number
				dc.b	$00				;Vendor Unique
				dc.b	$00				;Reserved
				dc.b	$00				;SCSI Interleave (MSB)
@interleave		dc.b	interleave		;SCSI Interleave (LSB)
				dcb.b	6,$00			;Reserved

				ENDIF

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

										;
										; Bad Parm Error
										;
@bad_parm		lda		#drvr_bad_parm
				sec
				rts

				ENDP

				EJECT
			
;*******************************************************
;
;	The EJECT command is used to cause the target to
;	eject the currently mounted device.  This could be a
;	CD Device, Tape Drive or Removable hard disk.  The
;	first thing that we will check is to see if the
;	removable bit is set.  It not then a DRVR_BAD_CODE
;	error will be returned.  Otherwise the call will be
;	sent to the device.  Any error, if any at all, will
;	be returned to the caller of this routine.
;
;	Called via 'JSR'
;
;	Inputs:		[dib_ptr]	=	Target DIB	l	(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Returns via 'RTS'
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;	or
;				Acc			=	Error
;				Carry		=	1
;
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************
			
				EXPORT	eject
eject			PROC

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

				IF			block_dvc = true\
				AND			character_dvc = false		THEN
										;
										; Verify request count.  Should be
										; zero
										;
				clc
				lda		<rqst_cnt
				bne		@bad_parm
										;
										; Check to see if it's removable.  If it
										; is then we will only mark it as offline,
										; no EJECT will be attempted.
										;
				ldy		#dib.dvcchar
				lda		[dib_ptr],y
				and		#removable
				clc
				beq		@mark_offline	;Carry Clear says do Switch
										;
										; Is this device linked to other
										; active devices?
										;
				jsr		chk_lnk_offline
				bcs		@mark_offline	;Yes.  Skip Eject and DISK SWITCH Calls
										;
										; Issue the eject call to the device.
										;
										; Set internal command flag
										;
				dec		|internal
										;
										; Tell the Main Driver where
										; our command structure resides.
										;

;* BD 201811
;* Eject with the standard #$1B
;* If it fails, eject with the non-standard $C0

				lda		#@eject
				sta		<scsi_mdrvr
				lda		#^@eject
				sta		<scsi_mdrvr+2
										;
										; Set the Call Type and Issue the
										; EJECT Command.
										;
				lda		#scsit_cont
				sta		|call_type
				jsr		|main_drvr
;				bcs		@rts

				IF		scsi_dtype = apple_cd\
				OR		scsi_dtype = changer		THEN

				bcc		@ejectOK

				clc
				lda		#@ejectC0		; If we're here, the eject $1B
				sta		<scsi_mdrvr		; returned in error
				lda		#^@ejectC0		; so, we try the non-standa
				sta		<scsi_mdrvr+2	; $C0 command

				lda		#scsit_cont
				sta		|call_type
				jsr		|main_drvr		; and we call the driver again
										;
				ENDIF
										; Issue DISK_SW Call. This is only done
										;	when the media is actually EJECTed
										;	in response to this call.
										;
@ejectOK		jsr		set_disk_sw
										;
										; Mark this DIB as Switched and Hard
										; Offline.
										;
				lda		#dvc_hardofl++\
						dvc_switch
				clc						;Do the Switch Call
				bra		@mark_offline2
										;
										; Mark this DIB as Switched only.
										;
@mark_offline	lda		#dvc_switch
				dec		leave_switched

@mark_offline2	ldy		#dib.dvcflag
				ora		[dib_ptr],y
				and		#dvc_online--\
						$ffff
				sta		[dib_ptr],y
										;
										; Issue DISK_SW Call. This is only done when
										; carry is clear which can only happen when
										; ejecting non-removable media or when the
										; media is actually EJECTed in response to
										; this call.
										;
				bcs		@no_switch
				jsr		set_disk_sw
										;
										; Clean Exit.
										;
@no_switch		lda		#no_error
				clc
				rts
										;
										; Bad request length.
										;
@bad_parm		lda		#drvr_bad_parm
				sec
										;
										; Exit.
										;
@rts			rts

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

				IF		scsi_dtype = direct_acc		THEN

										;
										; Command Packet
										;
@eject 			dc.b	$1B
				dc.b	$01				;IMMED Bit Set
				dcb.b	2,$00			;Reserved
				dc.b	$02				;Normal Unload. See document.
				dcb.b	7,$00

				ENDIF					;scsi_dtype = direct_acc

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

				IF		scsi_dtype = apple_cd\
				OR		scsi_dtype = changer	THEN

										;
										; Command Packet
										;
@eject 			dc.b	$1B
				dc.b	$00				;IMMED Bit Set
				dcb.b	2,$00			;Reserved
				dc.b	$02				;Normal Unload. See document.
				dcb.b	7,$00

@ejectC0		dc.b	$C0				; BD 201811 - renamed
				dcb.b	11,$00

				ENDIF					;scsi_dtype = apple_cd or changer

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

				IF		scsi_dtype = mcd_40	THEN

										;
										; Command Packet
										;
@eject			dc.b	$1B
				dc.b	$01				;IMMED Bit Set
				dcb.b	2,$00			;Reserved
				dc.b	$00				;Normal Unload. See document.
				dcb.b	7,$00

				ENDIF					;scsi_dtype = mcd_40

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

				ENDIF					;block_dvc = true

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

				IF			block_dvc = false\
				AND			character_dvc = true		THEN
										;
										; If the device is a Character Device,
										; then there is nothing to eject.
										;
				stz		<trans_cnt
				stz		<trans_cnt+2
				lda		#null
				clc
				rts

				ENDIF					;character_dvc = true

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

				ENDP

				EJECT
			
;*******************************************************
;
;	This call set the configuration parms for the
;	specified device.  The parameters are device
;	specific.  The list is preceded by a word length
;	which must be equal to the transmit count.  If not
;	then a DRVR_BAD_PARM error is returned.
;
;	Called via 'JSR'
;
;	Inputs:		[dib_ptr]	=	Target DIB	l	(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Returns via 'RTS'
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;	or
;				Acc			=	Error
;				Carry		=	1
;
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************
			
				EXPORT	s_config_parms
s_config_parms	PROC

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

				IF			block_dvc = true\
				AND			character_dvc = false		THEN

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

				IF			scsi_dtype = apple_cd\
				OR			scsi_dtype = changer	THEN
											;
											; Exit Write Protect Error.
											;
				lda		#drvr_wrt_prot
				sec
				rts

				ENDIF						;scsi_dtype = apple_cd or changer

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

				IF			scsi_dtype = mcd_40	THEN
											;
											; Exit Bad Call Error.
											;
				lda		#drvr_bad_code
				sec
				rts

				ENDIF						;scsi_dtype = mcd_40

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

				IF			scsi_dtype = direct_acc	THEN

;				INCLUDE		'SCSI Set Config'

				lda		#null
				sta		<trans_cnt
				sta		<trans_cnt+2
				clc
				rts

				ENDIF						;scsi_dtype = direct_acc

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

				ELSE						;block_dvc = False

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

											;
											; Lie for now.  Say that we did even
											; though we didn't.
											;
				clc
				lda		<rqst_cnt			;*************************
				beq		@rts				;*************************
											;
											; Bad request length.
											;
				lda		#drvr_bad_parm
				sec
											;
											; Exit.
											;
@rts			rts

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

				ENDIF						;block_dvc = true

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

				IF			scsi_dtype = direct_acc	THEN

				INCLUDE		'SCSI Set Vol/Disk'

				ENDIF						;scsi_dtype = direct_acc

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


				ENDP

				EJECT
			
;*******************************************************
;
;	This call is used to set the Wait or No Wait state
;	for a character device.  If in wait mode the device
;	will wait until request count bytes have been read
;	before returning to the caller.  Block devices will
;	return no error for this call.
;
;	Called via 'JSR'
;
;	Inputs:		[dib_ptr]	=	Target DIB	l	(LONG)
;				[buff_ptr]	=	Wait flag
;								$0000	=	Wait Mode
;								$8000	=	No Wait Mode
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Returns via 'RTS'
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;	or
;				Acc			=	Error
;				Carry		=	1
;
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************
			
				EXPORT	s_wait_mode
s_wait_mode		PROC
										;
										; Check the parameters list.
										; Should contain a WORD
										;
				lda		<rqst_cnt
				cmp		#$0002
				bne		@bad_parm
										;
										; Is it a block device?
										;
				ldy		#dib.dvcchar
				lda		[dib_ptr],y
				and		#blk_device
				bne		@clean_exit
										;
										; It's a character device.  Do we
										; set or clear the wait bit?
										;
@char_dvc		lda		[buff_ptr]
				beq		@set_wait		;Set the Wait Mode
				cmp		#$8000
				bne		@bad_parm
										;
										; Clear the Wait mode to No Wait.
										;
				ldy		#dib.dvcflag
				lda		[dib_ptr],y
				and		#wait_mode--\	;Generate a mask for this bit
						$ffff
										;
										; Save it and exit.
										;
				bra		@set_state
										;
										; Set Wait Mode
										;
@set_wait		ldy		#dib.dvcflag
				lda		[dib_ptr],y
				ora		#wait_mode
										;
										; Save New Mode
										;
@set_state		sta		[dib_ptr],y
				lda		#$0002
				sta		<trans_cnt
				stz		<trans_cnt+2
										;
										; Clean Exit
										;
@clean_exit		lda		#null			;Clear the Acc.
				clc
				rts
										;
										; Bad Parm Error
										;
@bad_parm		lda		#drvr_bad_parm
				sec
				rts

				ENDP

				EJECT
			
;*******************************************************
;
;	The set format options call is not supported by this
;	driver.  No error is returned unless they send a
;	transfer length > 0000.
;
;	Called via 'JSR'
;
;	Inputs:		[dib_ptr]	=	Target DIB	l	(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Returns via 'RTS'
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;	or
;				Acc			=	Error
;				Carry		=	1
;
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************
			
				EXPORT	format_opt
format_opt		PROC

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

				IF			block_dvc = true\
				AND			character_dvc = false		THEN

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

				IF			scsi_dtype = apple_cd\
				OR			scsi_dtype = changer	THEN
										;
										; If the device is a CD_ROM, then there
										; are no format options.
				stz		<trans_cnt
				stz		<trans_cnt+2
				lda		#bad_dev_number
				sec
				rts

				ENDIF					;scsi_dtype = apple_cd or changer

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

				IF			scsi_dtype = mcd_40	THEN
										;
										; If the device is a Tape, then there
										; are no format options.
				stz		<trans_cnt
				stz		<trans_cnt+2
				lda		#bad_dev_number
				sec
				rts

				ENDIF					;scsi_dtype = mcd_40

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

				IF			scsi_dtype = direct_acc	THEN
										;
										; If we are partitioned, then no data.
										;
				ldy		#dib.dvcchar

				lda		[dib_ptr],y
				and		#linked_dvc
				beq		@no_link		;No.
				stz		<trans_cnt
				stz		<trans_cnt+2
				lda		#null
				clc
				rts
										;
										;
										; Check the count
										;
@no_link		lda		<rqst_cnt
				beq		@bad_parm
				cmp		#$0002
				bne		@bad_parm
				sta		<trans_cnt
				stz		<trans_cnt+2
										;
										; Set default Option.
										;
				lda		[buff_ptr]
				sta		current_fmt
										;
										; Exit.
										;
				lda		#null
				clc
				rts
										;
										; Bad request length.
										;
@bad_parm		lda		#drvr_bad_parm
				sec
				rts

				ENDIF					;scsi_dtype = direct_acc

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

				ENDIF					;block_dvc = true

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

				IF			block_dvc = false\
				AND			character_dvc = true		THEN
										;
										; If the device is a Character Device,
										; then there are no format options.
										;
				lda		#drvr_bad_code
				sec
				rts

				ENDIF					;character_dvc = true

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

				ENDP

				EJECT
			
;*******************************************************
;
;	Main Entry point to the 'Control' filter.  This
;	"Filter" is called when a Control Command comes in.
;	See the headers of the seperate sections for the
;	details of the commands.
;
;	Called via 'JSR'
;
;	Inputs:		[dib_ptr]	=	Target DIB	l	(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Returns via 'RTS'
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;	or
;				Acc			=	Error
;				Carry		=	1
;
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************
			
				EXPORT	assign_part
assign_part		PROC

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

				IF			block_dvc = true\
				AND			character_dvc = false		THEN

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

				IF			scsi_dtype = apple_cd\
				OR			scsi_dtype = changer	THEN
											;
											; Exit Write Protect Error.
											;
				lda		#drvr_wrt_prot
				sec
				rts

				ENDIF						;scsi_dtype = apple_cd or changer

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

				IF			scsi_dtype = mcd_40	THEN
											;
											; Exit Bad Call Error.
											;
				lda		#drvr_bad_code
				sec
				rts

				ENDIF						;scsi_dtype = mcd_40

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

				IF			scsi_dtype = direct_acc	THEN

											;
											; Validate that this DIB is from
											; a Partition Map Entry.
											;
				ldy		#dib.start_blk
				lda		[dib_ptr],y
				ldy		#dib.start_blk+2
				ora		[dib_ptr],y
				bne		@over
											;
											; Not from a partition.
											;
				lda		#bad_dev_number
				sec
				rts
											;
											; Get block that contains this
											; DIB's Partition Map Entry.
											;
@over			ldy		#dib.part_blk
				lda		[dib_ptr],y
				sta		|rpm_blk_num
				sta		|wpm_blk_num
											;
											; Tell the code that we have already
											; set the Block Number and that this
											; is not a startup call.
											;
				dec		|stat_cont			;This is from a Status or Control Call
				dec		|rebuild			;And not from a startup Call
											;
											; Issue the Read PM Block Call.
											;
				jsr		|read_pm_blk
											;
											; Restore Direct Page Values.
											;
				pha
				php
				jsr		set_our_dp
				plp
				pla
				bcs		@bad_exit			;There was an error.
											;
											; Is what we read a Partition Map?
											;
				lda		|pm.Sig\
						+internal_buff
				cmp		#Part_sig
				bne		@bad_exit			;Bad Data Read.
											;
											; Set a pointer to actual text.
											;
				clc
				lda		<buff_ptr
				adc		#$0002
				sta		<scsi_zp4
				lda		<buff_ptr+2
				adc		#null
				sta		<scsi_zp4+2
											;
											; Get length of data
											;
				lda		[buff_ptr]
				cmp		#$0020+1
				bge		@bad_exit		
				tay
				tax
				dey							;16 bit mode
				dex
											;
											; Are they the same?  If so we can
											; save a block write.
											;
@cmp_loop		lda		|pm.PartType\
						+internal_buff,y
				cmp		[scsi_zp4],y
				bne		@move_new
				dey
				bpl		@cmp_loop
				bra		@all_done
											;
											; Not the same.  Replace old with
												; the new.
											;
@move_new		txa
				tay
@move_loop		lda		[scsi_zp4],y
				sta		|pm.PartType\
						+internal_buff,y
				dey
				bpl		@move_loop
											;
											; Issue the Write PM Block Call.
											;
				jsr		|write_pm_blk
											;
											; Restore Direct Page Values.
											;
				pha
				php
				jsr		set_our_dp
				plp
				pla
				bcc		@all_done			;There was no error.
											;
											; Error exit point.
											;
@bad_exit		lda		#drvr_io
				sec
				rts
											;
											; Clean Exit
											;
@all_done		lda		#no_error
				clc
				rts

				ENDIF						;scsi_dtype = direct_acc

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

				ENDIF						;block_dvc = true

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

				IF			block_dvc = false\
				AND			character_dvc = true		THEN
											;
											; If the device is a Character Device,
											; then there are no Partitions to
											; assign.
											;
				stz		<trans_cnt
				stz		<trans_cnt+2
				lda		#null
				clc
				rts

				ENDIF						;character_dvc = true

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

				ENDP

				EJECT
			
;*******************************************************
;
;	Entry point for the Arm Signal Drivcer Call.  This
;	call is not supported by this driver.  An error is
;	returned in all cases.
;
;	Called via 'JSR'
;
;	Inputs:		[dib_ptr]	=	Target DIB	l	(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Returns via 'RTS'
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;	or
;				Acc			=	Error
;				Carry		=	1
;
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************
			
				EXPORT	arm_signal
arm_signal		PROC

				lda		#drvr_bad_code
				sec
				rts

				ENDP

				EJECT
			
;*******************************************************
;
;	Entry point for the Disarm Signal Drivcer Call.
;	This call is not supported by this driver.  An error
;	is returned in all cases.
;
;	Called via 'JSR'
;
;	Inputs:		[dib_ptr]	=	Target DIB	l	(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Returns via 'RTS'
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;	or
;				Acc			=	Error
;				Carry		=	1
;
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************
			
				EXPORT	disarm_sig
disarm_sig		PROC

				lda		#drvr_bad_code
				sec
				rts

				ENDP

				EJECT
			
;*******************************************************
;
;	Most be the first device in a link!!!!
;
;	Entry point to the 'Set PMap' call.  This call
;	takes the information given by the caller on direct
;	page and builds the equivilent to a Write Status
;	Call to write request count bytes starting at
;	physical block number 1.  This is done by setting
;	the high bit of the partition call flag
;	'f_partition'.
;
;			Block Size				=	dib.blksize
;
;	We now Build the SCSI Main Driver Command and send
;	it.
;
;	The following will be validated by the Main Driver
;	when it builds the command. 
;
;			Request Count			=	Block Size * i
;			Block Number			=	Blk Num (No Offset)
;				This is for partitions.
;
;	After calling the Main driver and if no errors were
;	encountered, then the Transfer count will be
;	updated.
;
;	Called via 'JSR'
;
;	Inputs:		[dib_ptr]	=	Target DIB	l	(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Returns via 'RTS'
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;	or
;				Acc			=	Error
;				Carry		=	1
;
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************
			
				EXPORT	set_p_map
set_p_map		PROC

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

				IF			block_dvc = true\
				AND			character_dvc = false		THEN

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

				IF			scsi_dtype = apple_cd\
				OR			scsi_dtype = changer		THEN
											;
											; Exit Write Protect Error.
											;
				lda		#drvr_wrt_prot
				sec
				rts

				ENDIF						;scsi_dtype = apple_cd or changer

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

				IF			scsi_dtype = mcd_40	THEN
											;
											; Exit No Error.
											;
				lda		#drvr_bad_code
				sec
				rts

				ENDIF						;scsi_dtype = mcd_40

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

				IF			scsi_dtype = direct_acc	THEN

				stz		@do_532
											;
											; Set the Block size on Direct Page.
											; It is not placed there for this call,
											; and we should not rely on it being
											; left behind by the last call.
											;
				lda		#block_size
				sta		<blk_size
											;
											; Check if first DIB in link if any.
											; If it is zero, then we are already
											; there.  If not then error out.
											;
				ldy		#dib.headptr+2
				lda		[dib_ptr],y
				ldy		#dib.headptr
				ora		[dib_ptr],y
				bne		@bad_dev_num
											;
											; Let's check the request count.  If
											; this is $00000000, then exit clean
											; with no data transfered.
											;
				lda		<rqst_cnt
				ora		<rqst_cnt+2
				bne		@cnt_non_zero
				jmp		@out_of_here
											;
											; Verify Block Size.
											;
@cnt_non_zero	ldy		#dib.blksize
				lda		[dib_ptr],y			;Block Size
				cmp		<blk_size
				bne		@chk_532

				ldy		#dib.blksize+2
				lda		[dib_ptr],y
				beq		@blk_size_ok

@bad_parm		lda		#drvr_bad_parm
				sec
				rts

@bad_dev_num	lda		#bad_dev_number
				sec
				rts
											;
											; Check for 532 byte block size
											;
@chk_532		tax

				lda		<blk_size
				cmp		#block_size
				bne		@bad_parm

				cpx		#$0214
				bne		@bad_parm
				dec		@do_532

@blk_size_ok
											;
											; Build the (Write Data)
											; Control Command $800A
				lda		#$0001				; Sent to me Low >> High.
				xba							; Send it out High >> Low.
				sta		|c_block_num
											;
											; Set Main Driver Pointer to
											; our data for the command.
											;
				lda		#cmd_$800A
				sta		<scsi_mdrvr
				lda		#^cmd_$800A
				sta		<scsi_mdrvr+2
											;
											; Set the Partition call flag
											;
				dec		|f_partition
											;
											; Call Main Driver
											;
				lda		#scsit_cont
				sta		|call_type
											;
											; Issue the call.
											;
				jsr		check_532_rw
				bcs		@rts
											;
											; Build the (Write Data) command
											; to write the Driver Descriptor
											; Map.
											;
@do_ddm			lda		#$5245				;Defined by MAC as $4552
				sta		|sense_data

				lda		#block_size
				sta		<rqst_cnt
				stz		<rqst_cnt+2
				xba
				sta		|sense_data+2		;Defined by MAC as $0200

				ldx		#$0040
@loop			stz		|sense_data+4,x
				dex
				dex
				bpl		@loop

				stz		|c_block_num
											;
											; Set our buffer pointer
											;
				lda		#sense_data
				sta		<buff_ptr
				lda		#^sense_data
				sta		<buff_ptr+2
											;
											; Set Main Driver Pointer to
											; our data for the command.
											;
				lda		#cmd_$800A
				sta		<scsi_mdrvr
				lda		#^cmd_$800A
				sta		<scsi_mdrvr+2
											;
											; Set the Partition call flag
											;
				dec		|f_partition
											;
											; Call Main Driver
											;
				lda		#scsit_cont
				sta		|call_type
											;
											; Issue the call.
											;
				jsr		check_532_rw
				bcs		@rts
											;
											; Rebuild the DIBs.
											; Trashing the Volumes.
											;
				dec		|trash_it
											;
				jsr		rebld_dibs			;Issues a DISK_SW for each rebuilt DIB.
											;
											; Reset The 'TRASH IT' Flag..
											;
				stz		|trash_it
											;
											; Restore the origonal Direct Page values.
											;
				jsr		set_our_dp
											;
											; Update Transfer Count.
											;
@out_of_here	lda		<rqst_cnt
				sta		<trans_cnt
				lda		<rqst_cnt+2
				sta		<trans_cnt+2
											;
											; Exit No Error.
											;
				lda		#$0000
				clc
@rts			rts
											;
											; Variables and storage for short call.
											;
@do_532			dc.w	null				;532 byte block flag
											;
											; Command Data for this call.
											;
cmd_$800A		dc.b	$0A
				dc.b	$00
c_block_num		dc.w	$0000
c_block_cnt		dc.b	$00
				dcb.b	7,$00

				ENDIF						;scsi_dtype = direct_acc

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

				ENDIF						;block_dvc = true

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

				IF			block_dvc = false\
				AND			character_dvc = true		THEN
											;
											; If the device is a Character Device,
											; then there are no Partitions to
											; write.
											;
				stz		<trans_cnt
				stz		<trans_cnt+2
				lda		#null
				clc
				rts

				ENDIF						;character_dvc = true

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

				ENDP

				EJECT
				
				END
