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

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

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

;	June 14,	1988	File started.
;	June 21,	1988	Modified to mach the results of
;						the Code Review and added
;						register comments.

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

				IMPORT		unit_state
				IMPORT		test_unit_rdy
				IMPORT		auto_sense_data
				IMPORT		main_drvr
				IMPORT		call_type
				IMPORT		w_update_cache
				IMPORT		divend
				IMPORT		divsor
				IMPORT		result
				IMPORT		max_blk_cnt
				IMPORT		divide
				IMPORT		check_532_rw

				PRINT		OFF

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

				EJECT
			
;*******************************************************
;
;	Main Entry point to the 'Write' filter.  This
;	"Filter" takes the information given by the caller
;	on direct page and builds the equivilent to a Write
;	Extended Control Call.  In order of appearence:
;
;			Verify that Device #	­	$0000
;			Call Number				=	$0003
;			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	=	GS/OS Direct Page
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************

				EXPORT	Write
Write			PROC

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

				IF			scsi_dtype = scanner	THEN
										;
										; Device is not writable.
										;
				lda		#drvr_bad_req
				sec
				rts

				ENDIF

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

				IF			scsi_dtype = apple_cd\
				OR			scsi_dtype = changer		THEN
										;
										; Device is not writable.
										;
				lda		#drvr_wrt_prot
				sec
				rts

				ENDIF

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

				IF			scsi_dtype <> apple_cd\
				AND			scsi_dtype <> changer\
				AND			scsi_dtype <> scanner	THEN

				stz		@cache
										;
										; Is the device Removable?
										;
				ldy		#dib.dvcchar

				lda		[dib_ptr],y
				and		#removable
				beq		@write			;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?
										;
				ldy		#dib.dvcflag

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

				lda		[dib_ptr],y
				and		#dvc_online
				bne		@write			;Yes.
										;
										; Device is currently offline.
										;
@off_line		lda		#drvr_off_line
				sec
@rts_out		rts
										;
										; Let's check the request count.  If
										; this is $00000000, then exit clean
										; with no data transfered.
										;
@write			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		@completed
										;
										; 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

				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
				bcs		@rts_now

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

				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
										;
										; Regardless of the Cache Priority,
										; we need to see if it is in the cache.
										;
										; Set flag to cache the block only if
										; it is already there.
										;
				dec		@cache

				lda		<cache_prio		;Deferred Mode?
				bmi		@deferred		;Yes.

				ENDIF

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

@no_cache
										;
										; Build the (Write Data) Status
										; Command $802A, and just in case
										; it is not accepted, we will also
										; build $800A.
										;
				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_$802A
				sta		<scsi_mdrvr
				lda		#^cmd_$802A
				sta		<scsi_mdrvr+2
										;
										; Call Main Driver
										;
				lda		#scsit_cont
				sta		|call_type

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

				IF			scsi_dtype = direct_acc	THEN
										;
										; Issue the call.
										;
				jsr		check_532_rw
				bcc		@deferred		;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.

@deferred		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		<rqst_cnt
				sta		@orig_rqst
				sta		@rem_rqst
				sta		|divend
				lda		<rqst_cnt+2
				sta		@orig_rqst+2
				sta		@rem_rqst+2
				sta		|divend+2

				lda		<blk_size
				sta		|divsor

				jsr		divide
				bcc		@do_it
				rts
										;
										; Preserve Buffer pointer.  We will need to
										; bump it a few times.
										;
@do_it			lda		<buff_ptr
				sta		@orig_buff
				lda		<buff_ptr+2
				sta		@orig_buff+2
										;
										; 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_$800A
				sta		<scsi_mdrvr
				lda		#^cmd_$800A
				sta		<scsi_mdrvr+2
										;
										; Call Main Driver
										;
				lda		#scsit_cont
				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  *
										;* $80 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


				ELSE

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

				jsr		|main_drvr
				bcc		@completed

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

				IF			scsi_dtype = mcd_40	THEN

				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		#$0027			;This cover $27 Write Protected
				beq		@write_protect
				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!

@write_protect	ldy		#dib.dvcchar
				lda		[dib_ptr],y
				and		#write_allow--\
						$ffff
				sta		[dib_ptr],y

				lda		#drvr_wrt_prot
				sec
				rts

@not_ready		lda		#drvr_off_line
				sec
				rts

				ENDIF

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

				lda		#drvr_io
				rts						;There was an error!

@deferred

				ENDIF

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

										;
										; Update Transfer Count.
										;
@completed		lda		<rqst_cnt

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

				IF			block_dvc = true\
				AND			character_dvc = false		THEN

				and		#%1111111111111110

				ENDIF					;block_dvc = true

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

				sta		<trans_cnt
				lda		<rqst_cnt+2
				sta		<trans_cnt+2

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

				IF			cache_blks = true	THEN
										;
										; Check if cache bypassed.
										;
				bit		@cache
				bpl		@out_of_here	;Yes.

				clc
				lda		<cache_prio		;Deferred Mode?
				bmi		@chk_cache		;Yes.

				bne		@chk_cache		;No.

				sec						;Update only
@chk_cache		jsr		w_update_cache
				bcc		@out_of_here
				lda		<cache_prio		;Deferred Mode?
				bpl		@out_of_here	;No
				stz		@cache			;Yes. Force write skipping the cache
				jmp		@no_cache		;logic this time around.

				ENDIF

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

										;
										; Exit No Error.
										;
@out_of_here	lda		#$0000
				clc
@rts			rts

										;
										; Variables and storage for short call.
										;
@cache			dc.w	null			;Cache flag
@do_532			dc.w	null			;532 byte block flag

@orig_buff		dc.l	null			;Origonal Buffer Pointer
@orig_rqst		dc.l	null			;Origonal Request Count
@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_$800A		dc.b	$0A
				dc.b	$00
c_block_num_s	dc.w	$0000
c_block_cnt_s	dc.b	$00
				dcb.b	7,$00
										;
										; Command Data for this call.
										;
cmd_$802A		dc.b	$2A
				dc.b	$00
c_block_num_l	dc.l	$00000000
c_block_cnt_l	dcb.b	3,$00
				dcb.b	7,$00

				ENDIF

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

				ENDP

				EJECT
			
				END
