
;*******************************************************
;
;	SCSI Driver Get Volume/Disk Parms code.
;
;	Written by Matt Gulick.		Started April 17,1991
;
;	Copyright Apple Computer, Inc. 1990
;
;*******************************************************

;*******************************************************
;
;	This file contains the Get Volume/Disk Parms as
;	defined in the ERS.
;
;*******************************************************

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

;	April 17,	1991	File started.

				IMPORT		ddm_data
				IMPORT		drvr_size
				IMPORT		ddm_index
				IMPORT		error
				IMPORT		gc_buff_ptr

				ENTRY		pre_load_ddm
				ENTRY		find_drvr_part
				ENTRY		pdata_block

				EJECT
			
;*******************************************************
;
;	'g_vol_parms'
;
;	This routine is used to get information about the
;	volume in question.  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.
;
;*******************************************************

				EXPORT		gvp
gvp
											;
											; Check the Request Count.  We need at
											; least 4 bytes.
											;
				lda		<rqst_cnt+2
				bne		@rqst_cnt_ok
				lda		<rqst_cnt
				cmp		#$0002
				blt		@bad_cnt
											;
											; Check to see if this Volume is Online.
											;
@rqst_cnt_ok	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
				beq		@exit_parm
											;
											; Get block that contains this
											; DIB's Partition Map Entry.
											;
				ldy		#dib.part_blk
				lda		[dib_ptr],y
				sta		|rpm_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.
				jsr		set_our_dp
											;
											; Is what we read a Partition Map?
											;
				lda		|pm.Sig\
						+internal_buff
				cmp		#Part_sig
				bne		@bad_exit			;Bad Data Read.
											;
											; Get the Status Byte
											;
				lda		|pm.PartStatus+2\
						+internal_buff
				xba
				and		#$00ff
				sta		[buff_ptr]
											;
											; Clean Exit
											;
				lda		#$0002
				sta		<trans_cnt
				lda		#no_error
				sta		<trans_cnt+2
				clc
				rts
											;
											; 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

				EJECT
			
;*******************************************************
;
;	'g_disk_parms'
;
;	This routine is used to get information about the
;	disk in question.  The DIB Pointer must point to
;	the head DIB if this is a linked device.
;
;	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.
;
;*******************************************************

				EXPORT		gdp
gdp
											;
											; Check the Request Count.  Calls need
											; at least  and that will be verified there.
											;
				lda		<rqst_cnt+2
				bne		@rqst_cnt_ok
				lda		<rqst_cnt
				cmp		#$001A
				blt		@bad_cnt
											;
											; Check to see if this Volume is Online.
											;
@rqst_cnt_ok	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		do_gdisk
											;
											; Not from a partition.
											;
				bra		@exit_parm
											;
											; 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
											;
											; Get Disk Calls are handled here.
											;
do_gdisk		stz		|error
											;
											; Preserve the Users Buffer Pointer.
											;
				lda		<buff_ptr
				sta		gc_buff_ptr
				lda		<buff_ptr+2
				sta		gc_buff_ptr+2
											;
											; If a call is unacceptable an error will
											; be returned and the transfer length
											; will reflect the buffer size that is
											; needed if the current one is too small.
											;
											; Check Get DDM Info
											;
				ldy		#disk_pl.ddm_buff_len
				lda		[buff_ptr],y
				bne		@chk_ddm_len
				ldy		#disk_pl.ddm_buff_ptr
				ora		[buff_ptr],y
				ldy		#disk_pl.ddm_buff_ptr+2
				ora		[buff_ptr],y
				bne		@bad_ddm_len
				bra		@pre_load_ddm
											;
											; Is the DDM Buffer Long enough?
											;
@chk_ddm_len	cmp		#$0200
				bge		@do_ddm
											;
											; Bad request length for the DDM
											;
@bad_ddm_len	lda		#$0200						
				ldy		#disk_pl.ddm_trns_len
				sta		[buff_ptr],y
				lda		#drvr_bad_cnt
				sta		|error
				bra		@pre_load_ddm
											;
											; Get DDM.
											;
@do_ddm			jsr		get_ddm
				bcc		@chk_vol
				brl		@disk_done
											;
											; If called, the user is not
											; requesting the DDM and we need
											; to load it into our own buffer.
											;
@pre_load_ddm	jsr		pre_load_ddm
				bcc		@chk_vol
				brl		@disk_done
											;
											; Check Get Volume Info
											;
@chk_vol		ldy		#disk_pl.vol_buff_len
				lda		[buff_ptr],y
				bne		@chk_vol_len
				ldy		#disk_pl.vol_buff_ptr
				ora		[buff_ptr],y
				ldy		#disk_pl.vol_buff_ptr+2
				ora		[buff_ptr],y
				bne		@bad_vol_len
				bra		@chk_drvr
											;
											; Is the DDM Buffer Long enough?
											;
@chk_vol_len	cmp		#$0200
				bge		@do_vol
											;
											; Bad request length for the DDM
											;
@bad_vol_len	lda		#$0200						
				ldy		#disk_pl.vol_trns_len
				sta		[buff_ptr],y
				lda		#drvr_bad_cnt
				sta		|error
				bra		@chk_drvr
											;
											; Get Volume Info.
											;
@do_vol			jsr		get_vol
				bcs		@disk_done
											;
											; Check Get Driver Info
											;
@chk_drvr		clc
				lda		|ddm_index
				adc		#$0004
				tay
				lda		[ddm_buff],y
				xba
				asl		a					;Convert Blocks to Bytes
				stz		|drvr_size
				stz		|drvr_size+2
				sta		|drvr_size+1
											;
											; Check Get Driver Bit
											;
				ldy		#disk_pl.drvr_buff_len
				lda		[buff_ptr],y
				bne		@chk_drvr_len
				ldy		#disk_pl.drvr_buff_ptr
				ora		[buff_ptr],y
				ldy		#disk_pl.drvr_buff_ptr+2
				ora		[buff_ptr],y
				bne		@bad_drvr_len
				bra		@disk_done1
											;
											; Is the DDM Buffer Long enough?
											;
@chk_drvr_len	cmp		|drvr_size
				bge		@do_drvr
											;
											; Bad request length for the DDM
											;
@bad_drvr_len	lda		|drvr_size					
				ldy		#disk_pl.drvr_trns_len
				sta		[buff_ptr],y
				lda		#drvr_bad_cnt
				bra		@disk_done
											;
											; Get Volume Info.
											;
@do_drvr		jsr		get_drvr
											;
											; All done.  Return the transfer
											; length and any error codes that
											; were encountered
											;
@disk_done		sta		|error
@disk_done1		lda		#$001A
				sta		<trans_cnt
				stz		<trans_cnt+2
											;
											; Replace the Buffer Pointer.
											;
				lda		gc_buff_ptr
				sta		<buff_ptr

				lda		gc_buff_ptr+2
				sta		<buff_ptr+2

				lda		|error
				cmp		#$0001
				rts
											;
											; Subroutines for this code.
											;
											; The first Routine is used to Read
											; the DDM into the callers buffer.
											;
get_ddm			jsr		set_drvr_ndex
											;
											; Tell the Main Driver where
											; our command structure resides.
											;
				lda		#read_ddm
				sta		<scsi_mdrvr
				lda		#^read_ddm
				sta		<scsi_mdrvr+2
											;
											; Set the user's buffer
											;
				ldy		#disk_pl.ddm_buff_ptr
				lda		[buff_ptr],y
				tax

				ldy		#disk_pl.ddm_buff_ptr+2

				lda		[buff_ptr],y
				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

				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
											;
											; Is what we read a Driver
											; Descriptor Map?
											;
				lda		[ddm_buff]
				cmp		#DDM_sig
				bne		@bad_exit1			;Bad Data Read.
											;
											; Add in the Length
											;
				lda		#block_size
				ldy		#disk_pl.ddm_trns_len
				sta		[buff_ptr],y
											;
											; Clean Exit.
											;
				lda		#$0000
				clc
				rts
											;
											; Error exit point.
											;
@bad_exit1		jsr		set_our_dp
				lda		#drvr_io
				sec
				rts
											;
											; 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 Driver Index for which Driver
											; they want and use it to set the
											; ddm_index.
											;
				EXPORT		pre_load_ddm
pre_load_ddm	jsr		set_drvr_ndex
											;
											; 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 is called by both Get_DDM and Pre_Load_DDM and is used to set the index into the DDM for the requested driver.
											;
				EXPORT		set_drvr_ndex
set_drvr_ndex	ldy		#disk_pl.drvr_num
				lda		[buff_ptr],y
				and		#$000F				;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
				clc
				adc		#$0012				;Account for the $12 bytes of header
				sta		ddm_index
				rts
											;
											; This Routine is used to get the
											; Volume Info into the callers buffer.
											;
get_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
											;
											; Set the user's buffer
											;
				ldy		#disk_pl.vol_buff_ptr
				lda		[buff_ptr],y
				tax

				ldy		#disk_pl.vol_buff_ptr+2

				lda		[buff_ptr],y
				sta		<buff_ptr+2
				sta		<config_buff+2
				tay
				stx		<buff_ptr
				stx		<config_buff

				lda		#$0001				;Start looking here.
				jsr		find_drvr_part
				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
											;
											; Is what we read part of a
											; Partition Map?
											;
				lda		[config_buff]
				cmp		#Part_sig
				bne		@bad_exit1			;Bad Data Read.
											;
											; Add in the Length
											;
				lda		#block_size
				ldy		#disk_pl.vol_trns_len
				sta		[buff_ptr],y
											;
											; Clean Exit.
											;
				lda		#$0000
				clc
@rts			rts
											;
											; Error exit point.
											;
@bad_exit1		jsr		set_our_dp
				lda		#drvr_io
				sec
				rts
											;
											; Data for the READ BLOCK Command
											;
@read_vol		dc.b		$08
				dc.b		null
@read_vol_num	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

											;
											; This Routine is used to get the
											; Driver Data into the callers buffer.
											;
get_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		#$0008				;OR in the command number
				sta		@read_drvr

				iny
				iny
				lda		[ddm_buff],y
				sta		@block_num

				iny
				iny
				lda		[ddm_buff],y
				xba
				asl		a					;Cheap Multiply by $200
				stz		<rqst_cnt
				stz		<rqst_cnt+2		
				sta		<rqst_cnt+1
											;
											; Set the user's buffer
											;
				ldy		#disk_pl.drvr_buff_ptr
				lda		[buff_ptr],y
				tax

				ldy		#disk_pl.drvr_buff_ptr+2

				lda		[buff_ptr],y
				sta		<buff_ptr+2
				sta		<config_buff+2
				stx		<buff_ptr
				stx		<config_buff
											;
											; Tell the Main Driver where
											; our command structure resides.
											;
@length_ok		lda		#@read_drvr
				sta		<scsi_mdrvr
				lda		#^@read_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
											; 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
											;
											; Add in the Length
											;
				lda		<rqst_cnt
				ldy		#disk_pl.drvr_trns_len
				sta		[buff_ptr],y
											;
											; Clean Exit.
											;
				lda		#$0000
				rts
											;
											; Error exit point.
											;
@bad_exit1		jsr		set_our_dp
				lda		#drvr_io
				sec
				rts
											;
											; Command to Read the Driver Data
											;
@read_drvr		dc.b		$08
				dc.b		null
@block_num		dc.w		null
				dcb.b		10,null

				EJECT
