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

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

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

;	June 13,	1988	File started.
;	June 20,	1988	Registers in and out are

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

				IMPORT		unit_state
				IMPORT		main_drvr
				IMPORT		call_type
				IMPORT		r_get_cache
				IMPORT		r_all_in_cache
				IMPORT		divend
				IMPORT		divsor
				IMPORT		result
				IMPORT		max_blk_cnt
				IMPORT		m_blk_size
				IMPORT		m_blk_cnt
				IMPORT		calc_bytes
				IMPORT		m_rslt
				IMPORT		gsos_dpage
				IMPORT		divide
				IMPORT		check_532_rw
				IMPORT		chk_play_mode
				IMPORT		auto_sense_data
				IMPORT		sense_data
				IMPORT		open_flag
				IMPORT		get_data_status
				IMPORT		internal_buff
				IMPORT		scratch0
				IMPORT		internal

				PRINT		OFF

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

				EJECT

				IF			block_dvc = true\
				AND			character_dvc = false		THEN
			
;*******************************************************
;
;	Main Entry point to the 'Read' filter.  This
;	"Filter" takes the information given by the caller
;	on direct page and builds the equivilent to a Read
;	Extended Status Call.  In order of appearence:
;
;			Verify that Device #	­	$0000
;			Call Number				=	$0002
;			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 + Offset
;				This is for partitions.
;
;	After calling the Main driver and if no errors were
;	encountered, then the Transfer count will be
;	updated.
;
;	Inputs:		None.
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************

				ENDIF

				IF			character_dvc = true\
				AND			block_dvc = false		THEN
			
;*******************************************************
;
;	Main Entry point to the 'Read' filter.  This
;	"Filter" takes the information given by the caller
;	on direct page and builds the equivilent to a Read
;	Extended Status Call.  In order of appearence:
;
;			Verify that Device #	­	$0000
;			Call Number				=	$0002
;			Block Size				=	$0000
;			Device Opened			=	True
;
;	We now Build the SCSI Main Driver Command and send
;	it.
;
;	After calling the Main driver and if no errors were
;	encountered, then the Transfer count will be
;	updated.
;
;	Inputs:		None.
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************

				ENDIF

				EXPORT	Read
Read			PROC

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

				IF			block_dvc = true\
				AND			character_dvc = false		THEN

				stz		@cache
										;
										; 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
				bcs		@rts_out
										;
										; Is the device online?
										;
@online			ldy		#dib.dvcflag

				lda		[dib_ptr],y
				and		#dvc_hardofl
				bne		@off_line		;Yes.

				lda		[dib_ptr],y
				and		#dvc_online
				bne		@read			;Yes.
										;
										; Device is currently offline.
										;
@off_line		lda		#drvr_off_line
				sec
@rts_out		rts

										;
										; Preserve entry values.  This will
										; help restore the environment when
										; we exit.  To much is going on for
										; speeds sake to rely on these
										; locations.
										;
@read			lda		<rqst_cnt
				sta		@orig_rqst
				lda		<rqst_cnt+2
				sta		@orig_rqst+2
										;
										; Preserve Buffer pointer in case we
										; need to bump it.
										;
				lda		<buff_ptr
				sta		@orig_buff
				lda		<buff_ptr+2
				sta		@orig_buff+2
										;
										; Preserve starting Block Number in case
										; we need to bump it.
										;
				lda		<block_num
				sta		@orig_strt_blk
				lda		<block_num+2
				sta		@orig_strt_blk+2

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

				IF			scsi_dtype = apple_cd\
				OR			scsi_dtype = changer		THEN
										;
										; If the device is a CD_ROM device
										; then check to see if it is in audio
										; play mode.  If not clear the play
										; flag, otherwise set the audio play
										; flag.  This is used to prevent reads
										; while the audio tracks are playing.
										;
										; Is the device in Play Mode?
										;
				jsr		chk_play_mode

				ldy		#dib.dvcflag
				bcs		@no_play_mode

				lda		#play_mode
				ora		[dib_ptr],y
				bra		@store_play

@no_play_mode	lda		#play_mode--\
						$ffff
				and		[dib_ptr],y

@store_play		sta		[dib_ptr],y

				bcs		@read_2			;No.
										;
										; Device is Busy in PLAY MODE..
										;
				lda		#drvr_busy
				sec
				rts

@read_2
				ENDIF

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

										;
										; Let's check the request count.  If
										; this is $00000000, then exit clean
										; with no data transfered.
										;
				lda		<rqst_cnt
				and		#$01ff			;Cheap Check for multiple of 512
				bne		@bad_rqst_cnt
				lda		<rqst_cnt
				ora		<rqst_cnt+2
				bne		@chk_max
				jmp		@out_of_here
										;
										; Block Number is beyond the range for this disk.
										;
@out_of_range	lda		#drvr_bad_blk
				sec
				rts
										;
										; Requested Byte Count not a multiple of 512
										;
@bad_rqst_cnt	lda		#drvr_bad_cnt
				sec
				rts
										;
										; Check to see if more is being
										; requested then we can give him.
										;
@chk_max		lda		<rqst_cnt
				sta		|divend
				lda		<rqst_cnt+2
				sta		|divend+2

				lda		#block_size
				sta		|divsor

				sec
				ldy		#dib.blkcnt
				lda		[dib_ptr],y
				sbc		<block_num
				sta		|max_blk_cnt
				tax

				ldy		#dib.blkcnt+2
				lda		[dib_ptr],y
				sbc		<block_num+2
				sta		|max_blk_cnt+2

				bcc		@out_of_range
				ora		|max_blk_cnt
				beq		@out_of_range

				jsr		divide
				bcc		@dont_fix_cnt

				lda		#block_size
				sta		|m_blk_size

				lda		|max_blk_cnt
				sta		|m_blk_cnt

				lda		|max_blk_cnt+2
				sta		|m_blk_cnt+2

				jsr		calc_bytes

				lda		|m_rslt
				sta		<rqst_cnt
				sta		@orig_rqst
				lda		|m_rslt+2
				sta		<rqst_cnt+2
				sta		@orig_rqst+2

@dont_fix_cnt

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

				IF			scsi_dtype = direct_acc	THEN

										;
										; 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_now		rts
										;
										; Check for 532 byte block size
										;
@chk_532		tax

				lda		<blk_size
				cmp		#block_size
				bne		@bad_parm

				cpx		#$0214
				beq		@no_cache
				bra		@bad_parm

				ELSE

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

										;
										; Verify Block Size.
										;
@cnt_non_zero	ldy		#dib.blksize
				lda		[dib_ptr],y		;Block Size
				cmp		<blk_size
				bne		@bad_parm

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

@bad_parm		lda		#drvr_bad_parm
				sec
@rts_now		rts

				ENDIF

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

@blk_size_ok

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

				IF			cache_blks = true	THEN
										;
										; Skip Cache if FST Number >$8000
										; Otherwise check to see if they are
										; all in the cache.  If not we will
										; need to issue a read call.  If they
										; are all there we will skip the read
										; and go to the post processing
										; section.
										;
				bit		<fst_num		;Force Device Access?
				bmi		@no_cache		;Yes.

				dec		@cache

				jsr		r_all_in_cache	;Are they all there?
				bcc		@all_there		;Yes.

				ENDIF

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

@no_cache
										;
										; Build the (Read Data) Status
										; Command $8028, and just in case
										; it is not accepted, we will also
										; build $8008.
										;
				lda		<block_num		; Sent to me Low >> High.
				xba						; I Send it out High >> Low.
				sta		|c_block_num_l+2
				sta		|c_block_num_s
				lda		<block_num+2
				xba	
				sta		|c_block_num_l
										;
										; Set Main Driver Pointer to
										; our data for the command.
										;
				lda		#cmd_$8028
				sta		<scsi_mdrvr
				lda		#^cmd_$8028
				sta		<scsi_mdrvr+2
										;
										; Call Main Driver
										;
				lda		#scsit_stat
				sta		|call_type

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

				IF			scsi_dtype = direct_acc	THEN
										;
										; Issue the call.
										;
				jsr		check_532_rw
				bcc		@all_there		;Everything went ok.
				cmp		#drvr_bad_code	;Was it a Bad Command?
				beq		@munge			;Yes.  Do the Munging
				brl		@crapped_out2	;No. Then get out of here.

@all_there		jmp		@completed
										;
										; At this point the extended call was not
										; excepted.  We must now issue the normal
										; version of this command.  First, we must
										; check to see if the request is within the
										; one byte block count range.  If not, we
										; will need to special process this request.
										;
										; Setup Divide routine while preserving the
										; origonal count for later.
										;
@munge			lda		@orig_rqst
				sta		@rem_rqst
				sta		|divend
				lda		@orig_rqst+2
				sta		@rem_rqst+2
				sta		|divend+2

				lda		<blk_size
				sta		|divsor

				jsr		divide
				bcs		@rts_0
										;
										; Get result in block count.  If > $80,
										; then we need to break it into multiple
										; calls.  If not, then send it as is.
										;
				lda		|result+1
				bne		@gr8er_80
				lda		|result
				dec		a
				cmp		#$0080
				blt		@issue_call
										;
										; Set the max count for a single byte block
										; count (Block size * $80).
										;
@gr8er_80		lda		<blk_size+1
				and		#$00ff
				lsr		a
				sta		<rqst_cnt+2
				lda		<blk_size-1
				and		#$ff00
				ror		a
				sta		<rqst_cnt
										;
										; Set Main Driver Pointer to
										; our data for the command.
										;
@issue_call		lda		#cmd_$8008
				sta		<scsi_mdrvr
				lda		#^cmd_$8008
				sta		<scsi_mdrvr+2
										;
										; Call Main Driver
										;
				lda		#scsit_stat
				sta		|call_type
										;
										; Issue the call.
										;
				jsr		check_532_rw
				bcc		@did_munge
				sta		@error_loc
				lda		auto_sense_data+\
						rqst_sens.sense_key
				and		#$00ff			;Checking for $03
				cmp		#$0003			;This covers $03
				bne		@craped_out
										;
										; Some kind of I/O Error.
										;
@io_error		lda		#drvr_io
				sec
				rts

@craped_out		lda		@error_loc
@crapped_out2	and		#$7FFF
				cmp		#$7E00
				bge		@io_error
				sec
@rts_0			rts

@error_loc		dc.w	null
										;
										; Check for special processing.
										;
@did_munge								;
										; Get Transfer Result and bump values
										; for the next call.
										;
				clc
				ldy		#dib.trx_rqst
				lda		[dib_ptr],y
				sta		@trans_cnt
				adc		<buff_ptr
				sta		<buff_ptr
				ldy		#dib.trx_rqst+2
				lda		[dib_ptr],y
				sta		@trans_cnt+2
				adc		<buff_ptr+2
				sta		<buff_ptr+2
										;
										; Get remaining bytes to be transfered.
										;
				sec
				lda		@rem_rqst
				sbc		@trans_cnt
				sta		@rem_rqst
				lda		@rem_rqst+2
				sbc		@trans_cnt+2
				sta		@rem_rqst+2
				ora		@rem_rqst
				beq		@done

				clc						;Cause = values to result in Negative
				lda		@rem_rqst
				sbc		<rqst_cnt
				lda		@rem_rqst+2
				sbc		<rqst_cnt+2
				bpl		@over

				lda		@rem_rqst
				sta		<rqst_cnt
				lda		@rem_rqst+2
				sta		<rqst_cnt+2
				
										;
										;*****************************************
										;* Bump Block number.  Pay close         *
										;* attention to what this comment says.  *
										;* If you don't and you change the code  *
										;* then your on your own.  This word is  *
										;* in MSB >> LSB order.  We need to add  *
										;* $100 to it.  This is done by a simple *
										;* increment.                            *
										;*****************************************
										;
@over			lda		|c_block_num_s
				clc
				xba
				adc		#$0080
				xba
				sta		|c_block_num_s

				brl		@issue_call
										;
										; Restore the environment.
										;
@done			lda		@orig_rqst
				sta		<rqst_cnt
				lda		@orig_rqst+2
				sta		<rqst_cnt+2

				lda		@orig_buff
				sta		<buff_ptr
				lda		@orig_buff+2
				sta		<buff_ptr+2


				ENDIF

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

				IF			scsi_dtype = apple_cd\
				OR			scsi_dtype = changer		THEN

@redo_CDread	jsr		|main_drvr
				bcc		@all_there
				cmp		#$fe11
				beq		@redo_CDread

				lda		auto_sense_data+\		;Is it an audio disk?
						rqst_sens.addnl_sens_code
				and		#$00ff			;Checking for $84, $64, or $63
				cmp		#$0063			;This cover $63
				beq		@audio_disk
				cmp		#$0064			;This cover $64
				beq		@audio_disk
				cmp		#$0084			;This cover $84
				beq		@audio_disk

				lda		#drvr_io
				sec
				rts						;There was an error!

@audio_disk		lda		#drvr_no_dev
				sec
				rts
@all_there

				ENDIF

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

				IF			scsi_dtype = mcd_40	THEN

				jsr		|main_drvr
				bcc		@all_there

				lda		auto_sense_data+\
						rqst_sens.addnl_sens_code
				and		#$00ff			;Checking for $04, $A7, $A8, or $B0
				cmp		#$0004			;This cover $04
				beq		@not_ready
				cmp		#$00A7			;This cover $A7
				beq		@not_ready
				cmp		#$00A8			;This cover $A8
				beq		@not_ready
				cmp		#$00B0			;This cover $B0
				beq		@not_ready

				lda		#drvr_io
				sec
				rts						;There was an error!

@not_ready		lda		#drvr_off_line
				sec
				rts

@all_there

				ENDIF

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

@completed

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

				IF			cache_blks = true	THEN
										;
										; Check force device access Flag.  If
										; set, then exit out.
										;
				bit		@cache			;Forced Device Access?
				bpl		@out_of_here	;Yes.

										;
										; Check for CACHING.
										;
				clc						;Flag to force cache if needed.
				lda		<cache_prio		;Force Cache?
				bne		@do_cache		;Yes.
				sec						;Update only.
@do_cache		jsr		r_get_cache

				ENDIF

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

@out_of_here
										;
										; Restore the environment and
										; update Transfer Count.
										;
				ldx		|gsos_dpage
				lda		>rqst_cnt,x
				sta		<rqst_cnt
				lda		>rqst_cnt+2,x
				sta		<rqst_cnt+2

				lda		@orig_rqst
				sta		<trans_cnt
				lda		@orig_rqst+2
				sta		<trans_cnt+2

				lda		@orig_buff
				sta		<buff_ptr
				lda		@orig_buff+2
				sta		<buff_ptr+2

				lda		@orig_strt_blk
				sta		<block_num
				lda		@orig_strt_blk+2
				sta		<block_num+2
										;
										; Exit No Error.
										;
				lda		#$0000
				clc
@rts			rts

										;
										; Variables and storage for short call.
										;
@cache			dc.w	null			;Cache flag

@orig_buff		dc.l	null			;Origonal Buffer Pointer
@orig_rqst		dc.l	null			;Origonal Request Count
@orig_strt_blk	dc.l	null			;Origonal Starting Block Number
@rem_rqst		dc.l	null			;Number of bytes remaining
@trans_cnt		dc.l	null			;number of bytes transfered by device
										;this time
										;
										; Command Data for this call.
										;
cmd_$8028		dc.b	$28
				dc.b	$00
c_block_num_l	dc.l	$00000000
c_block_cnt_l	dcb.b	3,$00
				dcb.b	7,$00
										;
										; Command Data for this call.
										;
cmd_$8008		dc.b	$08
				dc.b	$00
c_block_num_s	dc.w	$0000
c_block_cnt_s	dc.b	$00
				dcb.b	7,$00

				ENDIF

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

				IF			character_dvc = true\
				AND			block_dvc = false		THEN

											;
											; Check to see if the Device is Opened.
											;
				lda		|open_flag
				beq		@not_open
											;
											; Is the device online?
											;
@open
				ldy		#dib.dvcflag

				lda		[dib_ptr],y
				and		#dvc_hardofl
				bne		@off_line			;Yes.

				lda		[dib_ptr],y
				and		#dvc_online
				bne		@read				;Yes.
											;
											; Device is currently offline.
											;
@off_line		lda		#drvr_off_line
				sec
@rts_out		rts
											;
											; Device is not Opened.
											;
@not_open		lda		#drvr_not_open
				sec
				rts

@read

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

				IF			scsi_dtype = scanner	THEN

											;
											; Save Requested amount for later.
											;
				lda		<rqst_cnt
				sta		@t_rqst_cnt			;Total Request Count (LOW)
				sta		@r_rqst_cnt			;Remaining Request Count (LOW)
				lda		<rqst_cnt+2
				sta		@t_rqst_cnt+2		;Total Request Count (HIGH)
				sta		@r_rqst_cnt+2		;Remaining Request Count (HIGH)

				lda		<buff_ptr
				sta		@orig_buffer
				lda		<buff_ptr+2
				sta		@orig_buffer+2

											;
											; Clear out Total Data Transfered Area.
											;
				stz		@t_transfered
				stz		@t_transfered+2
											;
											; Check to see if there is data to be had.
											;
@wait_loop		jsr		|get_data_status
				bcs		@io_error
				lda		|gds.data_avail\
						+sense_data
				ora		|gds.data_avail\
						+sense_data\
						+1
				beq		@wait_loop
				bra		@have_data
											;
											; Return an I/O Error.
											;
@io_error		lda		#drvr_io
				sec
				rts
											;
											; We have Data.  We need to check to see
											; if this is enough to give the user what
											; he requested.
											;
@have_data		stz		@enough_data

				lda		|gds.data_avail\	;Data is in High -> Low format.
						+sense_data\
						+1
				xba
				sta		|scratch0

				lda		|gds.data_avail\
						+sense_data\
						-1
				xba
				and		#$00ff
				sta		|scratch0+2

				sec
				lda		@r_rqst_cnt
				sbc		|scratch0
				sta		@stuff

				lda		@r_rqst_cnt+2
				sbc		|scratch0+2
											;
											; Was the request count < than what is
											; available in the scanner?  If so then
											; we will return request count bytes only.
											;
											; Were They equal?
											;
				blt		@do_rqst_cnt		;Yes.
				ora		@stuff
				beq		@do_rqst_cnt		;Yes.
											;
											; Well, it would apear that the request
											; count is > then what is available. We
											; will go ahead and get what is there
											; and then we will  check the wait flag.
											; If we are in wait state, then we will
											; loop around until we are able to get
											; what the user has requested. If we are
											; not in wait state, then we will exit
											; after the read.
											;
				ldx		|scratch0
				ldy		|scratch0+2

				sec
				lda		@r_rqst_cnt
				sbc		|scratch0
				sta		@r_rqst_cnt
				lda		@r_rqst_cnt+2
				sbc		|scratch0+2
				sta		@r_rqst_cnt+2

				dec		@enough_data
				bra		@do_read

@do_rqst_cnt	ldx		@r_rqst_cnt
				ldy		@r_rqst_cnt+2
				stz		@r_rqst_cnt
				stz		@r_rqst_cnt+2
											;
											; Set our Request Count for this call.
											;
@do_read		stx		<rqst_cnt
				sty		<rqst_cnt+2
											;
											; Set Main Driver Pointer to our data for the
											; command.
											;
				lda		#cmd_$8028
				sta		<scsi_mdrvr
				lda		#^cmd_$8028
				sta		<scsi_mdrvr+2
											;
											; It's a Status Call.
											;
				lda		#scsit_stat
				sta		|call_type
											;
											; Set Internal Command Flag
											;
				dec		|internal
											;
											; Issue the call.
											;
				jsr		|main_drvr
											;
											; Save transfer count.
											;
				clc
				ldy		#dib.trx_len
				lda		[dib_ptr],y
				adc		@t_transfered
				sta		@t_transfered
				ldy		#dib.trx_len+2
				lda		[dib_ptr],y
				adc		@t_transfered+2
				sta		@t_transfered+2
											;
											; Was there enough data?
											;
				lda		@enough_data
				beq		@no_wait			;Yes.
											;
											; Are we in WAIT Mode?
											;
				ldy		#dib.dvcflag
				lda		[dib_ptr],y
				and		#wait_mode
				beq		@no_wait			;No.
											;
											; Bump Buffer Pointer
											;
				clc
				ldy		#dib.trx_len
				lda		[dib_ptr],y
				adc		<buff_ptr
				sta		<buff_ptr
				ldy		#dib.trx_len+2
				lda		[dib_ptr],y
				adc		<buff_ptr+2
				sta		<buff_ptr+2
				
				jmp		@wait_loop			;Yes.
											;
											; Update transfer count.
											;
@no_wait		lda		@orig_buffer
				sta		<buff_ptr
				lda		@orig_buffer+2
				sta		<buff_ptr+2

				lda		@t_transfered
				sta		<trans_cnt
				lda		@t_transfered+2
				sta		<trans_cnt+2
											;
											; Clean Exit.
											;
				lda		#$0000
				clc
				rts
										
											; 
											; Data Area.
											;
@orig_buffer	dc.l	null				;Origonal Buffer Pointer
@t_rqst_cnt		dc.l	null				;TOTAL REQUEST COUNT
@r_rqst_cnt		dc.l	null				;REMAINING REQUEST COUNT
@t_transfered	dc.l	null				;AMOUNT ACTUALLY TRANSFERED
@enough_data	dc.w	null				;Is there enough data flag.
@stuff			dc.w	null				;Temporary Storage
											;
											; Command Description.
											;
cmd_$8028		dc.b	$28
rel_adr			dc.b	null				;Relative Address Bit is bit 0
t_data_type		dc.b	null				;Transfer Data Type
				dc.b	null				;Reserved
t_id			dc.w	null				;Transfer ID
t_length		dcb.b	3,null				;Transfer Length
control			dc.b	null				;Control Byte

				ENDIF

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

				ENDIF

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


				ENDP

				EJECT

				END
