
;*******************************************************
;
;	SCSI Driver Set Configuration Parms code.
;
;	Written by Matt Gulick.		Started June 22,1990
;
;	Copyright Apple Computer, Inc. 1990
;
;*******************************************************

;*******************************************************
;
;	This file contains the Set Configuration Parms as
;	defined in the ERS.
;
;*******************************************************

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

;	June 22,	1988	File started.

				EJECT
				IMPORT	ddm_data
			
;*******************************************************
;
;	's_config_parms'
;
;	This routine is used to set information about the
;	volume or disk in question.  If this call is issued
;	to set disk info, then the DIB Pointer must point to
;	the head DIB if this is a linked device.  Volume
;	calls should have the DIB Pointer set to the volume
;	for which the information is being requested.
;
;	The structure of the parameter list is defined in the
;	SCSI Driver ERS.  The parameters are going to depend
;	greatly on the type of device that this driver is
;	written for.  That means that the info for the Scanner
;	will not be the same in any form as that for a Hard
;	Disk, or a Tape drive.  These calls will be particular
;	for the device type supported.
;
;	Called via 'JSR'
;
;	Inputs:		[dib_ptr]	=	Target DIB			(LONG)
;				[buff_ptr]	=	Data Buffer Pointer	(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.
;
;*******************************************************

scp
											;
											; Check the Request Count.  Volume calls
											; need at least 4 bytes.  Disk calls need
											; more and that will be verified there.
											;
				lda		<rqst_cnt+2
				bne		@rqst_cnt_ok
				lda		<rqst_cnt
				cmp		#$0004
				blt		@bad_cnt
											;
											; Do a mini check of the parameter list
											; to see if it is correct.
											;
@rqst_cnt_ok	lda		[buff_ptr]			;Is the Request Count = 0?
				beq		@exit_none			;Yes.  Send Zero

				cmp		#$0002				;Is the count bigger than the min
				blt		@exit_parm			;Overhead Structure? No?  Parm Error.
											;
											; Check to see if this Volume is Online.
											;
				ldy		#dib.dvcflag
				lda		[dib_ptr],y
				and		#dvc_hardofl
				bne		@not_online
											;
											; 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
				beq		@exit_parm
											;
											; Check Bitmap.
											;
				ldy		#config_pl.bitmap
				lda		[buff_ptr],y
				bmi		@do_volume
				brl		do_disk
											;
											; Not from a partition.
											;
@not_online		lda		#drvr_off_line
				bra		@exit_none
											;
											; Error exit point.
											;
@bad_exit		jsr		set_our_dp
				lda		#drvr_io
				bra		@exit_none
											;
											; Error exit point.
											;
@bad_cnt		lda		#drvr_bad_cnt
				bra		@exit_none
											;
											; Use this code to exit when no data is
											; being returned.
											;
@exit_parm		lda		#drvr_bad_parm
@exit_none		stz		<trans_cnt
				stz		<trans_cnt+2
				cmp		#$0001
				rts
											;
											; Do the Volume Parms Here.
											; Acc = Bitmap on entry.
											;
@do_volume		sta		|bitmap
				and		#vconf_resv2++\
						vconf_resv3++\		;Check Bits that are not supported
						vconf_resv4++\		;at this time.
						vconf_resv5++\
						vconf_resv6
				bne		@exit_parm			;If unsupported bit set then error out.
											;
											; Check for Remount Bit
											;
				lda		|bitmap
				and		#vconf_remount
				beq		@next_0
				jsr		remount_vol
											;
											; Check Status Bits Flag.  If set, then we
											; use the low byte of this word to set the
											; status bits in the Partition Map and in
											; the DIB Status.
											;
@next_0			lda		|bitmap
				and		#vconf_set_stat
				beq		@next_1
				jsr		set_stat_bits

@next_1			bcs		@rts
				lda		#$0004
				sta		<trans_cnt
				lda		#null
				sta		<trans_cnt+2
				clc
@rts			rts
											;
											; Routines used by the above code to
											; perform the requested task.
											;
remount_vol
											;
											; Mark this DIB as Switched and Hard
											; Offline.
											;
				ldy		#dib.dvcflag
				lda		[dib_ptr],y
				ora		#dvc_online++\
						dvc_switch
				and		#dvc_hardofl--\
						$ffff
				sta		[dib_ptr],y
											;
											; Generate a Disk Switched Event
											;
				jsr		set_disk_sw
											;
											; Clean Exit.
											;
				rts

set_stat_bits
											;
											; Set the Status Bits in the Partition
											; Map entry to mach those supplied by
											; this call.
											;
											; Check to see if this Volume is Online.
											;
				ldy		#dib.dvcflag
				lda		[dib_ptr],y
				and		#dvc_online
				beq		@not_online
											;
											; 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		#drvr_bad_parm
				sec
				rts
											;
											; Not from a partition.
											;
@not_online		lda		#drvr_off_line
				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
				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 the Status Byte
											;
				lda		|bitmap
				and		#$00ff
				sta		@temp
				lda		|pm.PartStatus+2\
						+internal_buff
				xba
				and		#$ff00
				ora		@temp
				xba
				sta		|pm.PartStatus+2\
						+internal_buff
											;
											; Issue the Write PM Block Call.
											;
				jsr		|write_pm_blk
				bcc		@all_done			;There was no error.
											;
											; Error exit point.
											;
@bad_exit		jsr		set_our_dp
				lda		#drvr_io
				sec
				rts
											;
											; Clean Exit
											;
@all_done		jsr		set_our_dp

				lda		@temp				;Update the DIB.
				and		#vconf_wr_enable++\
						vconf_rd_enable
				asl		a
				sta		@temp
				ldy		#dib.dvcchar
				lda		[dib_ptr],y
				and		#write_allow++\
						read_allow--\
						$ffff
				ora		@temp
				sta		[dib_ptr],y
											;
											; Generate a Disk Switched Event
											;
				jsr		set_disk_sw

				lda		#no_error
				clc
				rts
											;
											; Data Area.
											;
@temp			dc.w	null

											;
											; Set Config Disk Calls are handled
											; here.
											;
											; Init Transfer Count to Null.  Then
											; check to be sure that this is a Set
											; Config ParmList and not a Get Config
											; List.
											;
do_disk			stz		length
				stz		length+2
											;
											; Get the Request Count.We will set this
											; aside and subtract from that as data
											; is transfered.  This way we will not
											; over run their buffers.
											;
				ldy		<rqst_cnt
				sty		rqst
				ldy		<rqst_cnt+2
				sty		rqst+2

				sta		temp
				and		#$4000
				beq		@exit_parm
											;
											; Check Count. Must have a count that
											; is at least big enough to cover the
											; count and bitmap fields plus the first
											; DDM_Info field.  If not then there is
											; an error.
											;
				ldy		#config_pl.count
				sec
				lda		[buff_ptr],y
				sbc		#config_pl.vol_info\
						-config_pl.count
				sta		count
				blt		@exit_parm
											;
											; Put the first flag bit in Bit 15.
											;%01xxx00000000000
				asl		temp				;%1xxx000000000000
				asl		temp				;%xxx0000000000000
											;
											; Preserve the Users Buffer Pointer.
											;
				lda		<buff_ptr
				sta		gc_buff_ptr
				lda		<buff_ptr+2
				sta		gc_buff_ptr+2
											;
											; Pre_load the DDM if needed.
											;
				jsr		pre_load_ddm
				bcc		@ddm_loaded
				rts
											;
											; Use this code to exit when no data is
											; being returned.
											;
@exit_parm		lda		#drvr_bad_parm
				sec
				rts
											;
											; Check Set DDM Bit
											;
@ddm_loaded		asl		temp
				bcc		@chk_vol
				jsr		set_ddm
				bcs		@disk_done
											;
											; Check Set Volume Info Bit
											;
@chk_vol		jsr		|chk_count
				bcs		@disk_done
				asl		temp
				bcc		@chk_drvr
				jsr		set_vol
				bcs		@disk_done
											;
											; Check Set Driver Bit
											;
@chk_drvr		jsr		|chk_count
				bcs		@disk_done
				asl		temp
				bcc		@disk_done
				jsr		set_drvr
											;
											; All done.  Return the transfer
											; length and any error codes that
											; were encountered
											;
@disk_done		pha
				lda		length
				sta		<trans_cnt
				lda		length+2
				sta		<trans_cnt+2
											;
											; Replace the Buffer Pointer.
											;
				lda		gc_buff_ptr
				sta		<buff_ptr

				lda		gc_buff_ptr+2
				sta		<buff_ptr+2

				pla
				cmp		#$0001
				rts
											;
											; Subroutines for this code.
											;
											; Check to see if the count contains
											; another entry.
											;
				EXPORT	chk_count
chk_count		sec
				lda		|count
				sbc		#config_pl.vol_info\
						-config_pl.ddm_info
				sta		|count
				clc
				bpl		@chk_done
				sec
@chk_done		rts
											;
											; The first Routine is used to Write
											; the DDM from the callers buffer.
											;
set_ddm
											;
											; Tell the Main Driver where
											; our command structure resides.
											;
				lda		#@write_ddm
				sta		<scsi_mdrvr
				lda		#^@write_ddm
				sta		<scsi_mdrvr+2
											;
											; Set the user's buffer
											;
				ldy		#config_pl.ddm_info
				clc

				lda		[buff_ptr],y
				sta		<config_tcnt
				adc		#$0004
				tax

				ldy		#config_pl.ddm_info+2

				lda		[buff_ptr],y
				sta		<config_tcnt+2
				adc		#$0000
				sta		<buff_ptr+2
				sta		<ddm_buff+2			;Incase we need it for the Driver Code
				stx		<buff_ptr
				stx		<ddm_buff			;Incase we need it for the Driver Code
											;
											; Is what we are writing a Driver
											; Descriptor Map?
											;
				lda		[ddm_buff]
				cmp		#DDM_sig
				bne		@bad_exit1			;Bad Data.
											;
											; And our length from the DIB's
											; Block Size value
											;
				lda		[config_tcnt]
				cmp		#block_size+4
				beq		@over_0
											;
											; Use this code to exit when no data is
											; being sent.
											;
@exit_parm1		lda		#drvr_bad_parm
				sec
				rts

@over_0			lda		#block_size
				sta		<rqst_cnt
				lda		#^block_size
				sta		<rqst_cnt+2
											;
											; Set internal command flag
											;
				dec		|internal
											;
											; Set the Partition call flag
											;
				dec		|f_partition
											;
											; Set the Call Type and Issue the
											; READ BLOCK Command.
											;
				lda		#scsit_cont
				sta		|call_type
											;
											; Issue the call.
											;
				jsr		check_532_rw
				php
											;
											; Replace the Buffer Pointer.
											;
				lda		gc_buff_ptr
				sta		<buff_ptr

				lda		gc_buff_ptr+2
				sta		<buff_ptr+2
											;
											; Check for Call Errors
											;
				plp
				bcs		@bad_exit1
											;
											; Add in the Length
											;
				clc
				lda		#block_size
				ldy		#$0002
				sta		[config_tcnt],y

				adc		length
				sta		length

				lda		length+2
				adc		#^block_size
				sta		length+2
											;
											; Clean Exit.
											;
				lda		#$0000
				clc
				rts
											;
											; Error exit point.
											;
@bad_exit1		jsr		set_our_dp
				lda		#drvr_io
				sec
				rts
											;
											; Data for the WRITE BLOCK Command
											;
@write_ddm		dc.b		$0A
				dc.b		null
				dc.w		null
				dcb.b		10,null
											;
											; Other Data Storage areas.
											;
				EXPORT		bitmap
bitmap			dc.w		null
				EXPORT		length
length			dc.l		null			;Total length of all data sent
				EXPORT		rqst
rqst			dc.l		null			;Total length of all data requested
				EXPORT		gc_buff_ptr
gc_buff_ptr		dc.l		null			;Their buffer pointer
				EXPORT		count
count			dc.w		null			;Parmlist count.  Will be adjusted as we go.
				EXPORT		temp
temp			dc.w		null
				EXPORT		error
error			dc.w		null
				EXPORT		ddm_index
ddm_index		dc.w		null			;Index Into the DDM for the rqsted DRVR
											;
											; This Routine is used to set the
											; Volume Info from the callers buffer.
											;
set_vol
											;
											; Find the Partition Map Entry that matches
											; the data blocks used in the DDM.
											;
				ldy		ddm_index
				lda		[ddm_buff],y
				sta		|pdata_block

				iny
				iny
				lda		[ddm_buff],y
				sta		|pdata_block+2

				lda		#$0001				;Start looking here.
				ldx		#internal_buff
				ldy		#^internal_buff
				jsr		find_drvr_part
				bcs		@rts
											;
											; Set the Partition Map Block Number
											; that we need to Write.
											;
				sta		@write_vol_num
											;
											; Tell the Main Driver where
											; our command structure resides.
											;
				lda		#@write_vol
				sta		<scsi_mdrvr
				lda		#^@write_vol
				sta		<scsi_mdrvr+2
											;
											; Set the user's buffer
											;
				ldy		#config_pl.vol_info
				clc

				lda		[buff_ptr],y
				sta		<config_tcnt
				adc		#$0004
				tax

				ldy		#config_pl.vol_info+2

				lda		[buff_ptr],y
				sta		<config_tcnt+2
				adc		#$0000
				sta		<buff_ptr+2
				sta		<config_buff+2
				stx		<buff_ptr
				stx		<config_buff
											;
											; Is what we are writing a Driver
											; Descriptor Map?
											;
				lda		[config_buff]
				cmp		#Part_sig
				bne		@bad_exit1			;Bad Data Read.
											;
											; And our length from the DIB's
											; Block Size value
											;
				lda		[config_tcnt]
				cmp		#block_size+4
				beq		@over_0
											;
											; Use this code to exit when no data is
											; being sent.
											;
@exit_parm1		lda		#drvr_bad_parm
				sec
@rts			rts

@over_0			lda		#block_size
				sta		<rqst_cnt
				lda		#^block_size
				sta		<rqst_cnt+2
											;
											; Set internal command flag
											;
				dec		|internal
											;
											; Set the Partition call flag
											;
				dec		|f_partition
											;
											; Set the Call Type and Issue the
											; READ BLOCK Command.
											;
				lda		#scsit_cont
				sta		|call_type
											;
											; Issue the call.
											;
				jsr		check_532_rw
				php
											;
											; Replace the Buffer Pointer.
											;
				lda		gc_buff_ptr
				sta		<buff_ptr

				lda		gc_buff_ptr+2
				sta		<buff_ptr+2
											;
											; Check for Call Errors
											;
				plp
				bcs		@bad_exit1
											;
											; Add in the Length
											;
				clc
				lda		#block_size
				ldy		#$0002
				sta		[config_tcnt],y

				adc		length
				sta		length

				lda		length+2
				adc		#^block_size
				sta		length+2
											;
											; Clean Exit.
											;
				lda		#$0000
				clc
				rts
											;
											; Error exit point.
											;
@bad_exit1		jsr		set_our_dp
				lda		#drvr_io
				sec
				rts
											;
											; Data for the WRITE BLOCK Command
											;
@write_vol		dc.b	$0A
				dc.b	null
@write_vol_num	dc.w	null
				dcb.b	10,null
											;
											; This Routine is used to set the
											; Driver Data into the callers buffer.
											;
set_drvr
											;
											; At this point the DDM is in Memory
											; and the Zero Page Pointer 'DDM_BUFF'
											; points to it.
											;
				ldy		ddm_index
				lda		[ddm_buff],y
				and		#%0001111100000000	;Allow Byte 1, bits 0-4 to pass
				ora		#$000A				;OR in the command number
				sta		@write_drvr

				iny
				iny
				lda		[ddm_buff],y
				sta		@block_num
											;
											; Set the user's buffer
											;
				ldy		#config_pl.driver_info
				clc

				lda		[buff_ptr],y
				sta		<config_tcnt
				adc		#$0004
				tax

				ldy		#config_pl.driver_info+2

				lda		[buff_ptr],y
				sta		<config_tcnt+2
				adc		#$0000
				sta		<buff_ptr+2
				sta		<config_buff+2
				stx		<buff_ptr
				stx		<config_buff
											;
											; And our length.
											;
				sec
				stz		<rqst_cnt+2
				lda		[config_tcnt]
				sbc		#$0004
				sta		<rqst_cnt
											;
											; Tell the Main Driver where
											; our command structure resides.
											;
				lda		#@write_drvr
				sta		<scsi_mdrvr
				lda		#^@write_drvr
				sta		<scsi_mdrvr+2
											;
											; Set internal command flag
											;
				dec		|internal
											;
											; Set the Partition call flag
											;
				dec		|f_partition
											;
											; Set the Call Type and Issue the
											; WRITE BLOCK Command.
											;
				lda		#scsit_cont
				sta		|call_type
											;
											; Issue the call.
											;
				jsr		check_532_rw
				php
											;
											; Replace the Buffer Pointer.
											;
				lda		gc_buff_ptr
				sta		<buff_ptr

				lda		gc_buff_ptr+2
				sta		<buff_ptr+2
											;
											; Check for Call Errors
											;
				plp
				bcs		@bad_exit1
											;
											; Add in the Length
											;
				clc
				lda		<rqst_cnt
				ldy		#$0002
				sta		[config_tcnt],y

				adc		length
				sta		length

				lda		length+2
				adc		<rqst_cnt+2
				sta		length+2
											;
											; Clean Exit.
											;
				lda		#$0000
				rts
											;
											; Error exit point.
											;
@bad_exit1		jsr		set_our_dp
				lda		#drvr_io
				sec
				rts
											;
											; Data for the I/O Commands.
											;
@temp			dc.w		null
											;
											; Command to Write the Driver Data
											;
@write_drvr		dc.b		$0A
				dc.b		null
@block_num		dc.w		null
				dcb.b		10,null
											;
											; Because all three sub-calls require a
											; DDM for information we must make sure
											; that this is available.  If they are
											; not setting the DDM we will load ity
											; from the disk.  If they are we will
											; use theirs as it is the most current.
											;
											; Check low byte of the Bitmap for which
											; Driver they want and use it to set the
											; ddm_index.
											;
				EXPORT	pre_load_ddm
pre_load_ddm
				ldy		#config_pl.bitmap
				lda		[buff_ptr],y
				and		#$200F				;Max of 15 Drivers in the DDM
				dec		a					;Dec so we can use for index
				asl		a					;*2
				asl		a					;*4
				asl		a					;*8
				php
				clc
				adc		#$0012				;Account for the $12 bytes of header
				sta		ddm_index
											;
											; If Carry is set, then the DDM is in
											; users memory and we do not need to
											; read it from the disk.
											;
				plp
				bcs		@ddm_loaded
											;
											; We need to load the DDM into the
											; internal buffer.
											;
											; Tell the Main Driver where
											; our command structure resides.
											;
				lda		#@read_ddm
				sta		<scsi_mdrvr
				lda		#^@read_ddm
				sta		<scsi_mdrvr+2
											;
											; Set our buffer
											;
				lda		#ddm_data
				sta		<buff_ptr
				sta		<ddm_buff
				lda		#^ddm_data
				sta		<buff_ptr+2
				sta		<ddm_buff+2
											;
											; And our Block Size value
											;
				lda		#block_size
				sta		<rqst_cnt
				lda		#^block_size
				sta		<rqst_cnt+2
											;
											; Set internal command flag
											;
				dec		|internal
											;
											; Set the Partition call flag
											;
				dec		|f_partition
											;
											; Set the Call Type and Issue the
											; READ BLOCK Command.
											;
				lda		#scsit_stat
				sta		|call_type
											;
											; Issue the call.
											;
				jsr		check_532_rw
				php
											;
											; Replace the Buffer Pointer.
											;
				lda		gc_buff_ptr
				sta		<buff_ptr

				lda		gc_buff_ptr+2
				sta		<buff_ptr+2
											;
											; Check for Call Errors
											;
				plp
				bcs		@bad_exit1

@ddm_loaded		clc
				rts
											;
											; Use this code to exit when no data is
											; being sent.
											;
@exit_parm		lda		#drvr_bad_parm
				sec
				rts
											;
											; Error exit point.
											;
@bad_exit1		jsr		set_our_dp
				lda		#drvr_io
				sec
				rts
											;
											; Command to Read the DDM if we
											; need it.
											;
@read_ddm		dc.b		$08
				dc.b		null
				dc.w		null
				dcb.b		10,null
											;
											; This routine will search for the
											; Partition Map entry that has the same
											; data block location as that for the
											; referenced entry in the DDM.
											;
											; Acc	= Block to start with
											; Y reg = High word of the Buffer to use
											; X reg = Low word of the Buffer to use
											;
				EXPORT	find_drvr_part
find_drvr_part
				xba
				sta		@block_num
				stx		<buff_ptr
				sty		<buff_ptr+2

											;
											; And our length.
											;
				stz		<rqst_cnt+2
				lda		#block_size
				sta		<rqst_cnt
											;
											; Tell the Main Driver where
											; our command structure resides.
											;
				lda		#@read_part
				sta		<scsi_mdrvr
				lda		#^@read_part
				sta		<scsi_mdrvr+2
											;
											; Set internal command flag
											;
@loop			dec		|internal
											;
											; Set the Partition call flag
											;
				dec		|f_partition
											;
											; Set the Call Type and Issue the
											; READ BLOCK Command.
											;
				lda		#scsit_stat
				sta		|call_type
											;
											; Issue the call.
											;
				jsr		check_532_rw
				php
											;
											; Is this the block that we want?
											;
				ldy		#pm.PyPartStart
				lda		[buff_ptr],y
				cmp		|pdata_block
				bne		@next
				ldy		#pm.PyPartStart+2
				lda		[buff_ptr],y
				cmp		|pdata_block+2
				bne		@next
											;
											; Replace the Buffer Pointer.
											;
				lda		gc_buff_ptr
				sta		<buff_ptr

				lda		gc_buff_ptr+2
				sta		<buff_ptr+2
											;
											; It's a match.  Pass it on in the Acc.
											;
				lda		@block_num
											;
											; Check for Call Errors
											;
				plp
				clc
				rts
											;
											; Not a match.  Do the next one only if
											; still in the range of the partition map.
											;
@next			plp
				lda		@block_num
				xba
				inc		a
				xba
				sta		@block_num
				ldy		#pm.MapBlkCnt+2
				cmp		[buff_ptr],y
				bne		@loop
											;
											; Not found.  Do Cleanup and exit
											; with an error.
											;
											; Replace the Buffer Pointer.
											;
				lda		gc_buff_ptr
				sta		<buff_ptr

				lda		gc_buff_ptr+2
				sta		<buff_ptr+2

				sec
				lda		#drvr_bad_blk
				rts
											;
											; Command to Read the DDM if we
											; need it.
											;
@read_part		dc.b		$08
				dc.b		null
@block_num		dc.w		null
				dcb.b		10,null

				EXPORT	pdata_block
pdata_block		dc.l	null

				EJECT
