
;*******************************************************
;
;	SCSI Driver Management Routines.
;
;	Written by Matt Gulick.		Started May 16,1988
;
;	Copyright Apple Computer, Inc. 1988,89
;
;*******************************************************

;*******************************************************
;
;	This file contains the subroutines needed by the
;	SCSI Driver for things such as Getting RAM, Building
;	data areas, calling outside routines for DIB
;	management, as well as other generic routines that
;	are used to make life easier for the driver.  Also
;	included in this file is the record definition for
;	the DIB structure including all it's extensions.
;
;*******************************************************

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

;	May 16, 1988	File started.
;	May 17,	1988	DIB Record defined.
;	June 6,	1988	Main Driver Written.
;	Jun 20,	1988	Update Input/Output Data in Comments
;	Apr	14,	1989	Added Conditional Logic to remove
;					block routines from character device
;					assemblies.

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

				IMPORT		stat_cont

				IMPORT		master_uid
				IMPORT		scsi_uid
				IMPORT		scsi_mgrnum
				IMPORT		rout2_s_disp
				IMPORT		hndl_offset
				IMPORT		buff_len
				IMPORT		page_cnt
				IMPORT		dpi_overide
				IMPORT		call_type
				IMPORT		main_drvr
				IMPORT		internal
;				IMPORT		disk_switch
				IMPORT		ram_page_cnt
				IMPORT		internal_buff
				IMPORT		default_dib
				IMPORT		first_time
				IMPORT		only_one
				IMPORT		part_cnt
				IMPORT		t_dvc_blocks
				IMPORT		f_partition
				IMPORT		part_num
				IMPORT		vPart_cnt
				IMPORT		rebuild
				IMPORT		trash_it
				IMPORT		direct_page
				IMPORT		exit_dpage
				IMPORT		gsos_dpage
				IMPORT		saved_zp
				IMPORT		valid
				IMPORT		dvc_ram
				IMPORT		sense_data
				IMPORT		dvc_count
				IMPORT		result
				IMPORT		divend
				IMPORT		divsor
				IMPORT		max_blk_cnt
				IMPORT		tot_dib_cnt
				IMPORT		dib_data_struct
				IMPORT		main_caller
				IMPORT		curr_hndl
				IMPORT		new_dib
				IMPORT		scratch0
				IMPORT		scratch1
				IMPORT		scratch2
				IMPORT		scratch3
				IMPORT		killer_blk
				IMPORT		auto_sense_data
				IMPORT		temp_acc
				IMPORT		pdi_flag
				IMPORT		new_list
				IMPORT		new_dib_cnt
				IMPORT		new_dib_list
				IMPORT		real_unit			;*************************

				IMPORT		m_blk_size
				IMPORT		m_blk_cnt
				IMPORT		m_rslt

				ENTRY		zero_mem
				ENTRY		get_space

				ENTRY		make_dib
				ENTRY		find_empty
				ENTRY		chk_ram

				ENTRY		rebld_dibs
				ENTRY		dibicise_new_ram
				ENTRY		dibicise_new_dib
				ENTRY		set_link_ptrs
				ENTRY		major_update
				ENTRY		minor_update

				ENTRY		fix_unit_1
				ENTRY		add_dib_ptr
				ENTRY		do_post_install

				ENTRY		read_pm_blk
				ENTRY		rpm_blk_num
;				ENTRY		wpm_blk_num
				ENTRY		chk_map
				ENTRY		no_partition
				ENTRY		check_map_entry

				ENTRY		c_strt_buffer
				ENTRY		c_strt_rqst_cnt
				ENTRY		c_strt_blk_num
				ENTRY		cache_valid
				ENTRY		put_in_cache
				ENTRY		get_from_cache

				ENTRY		test_unit_rdy
				ENTRY		read_capacity
				ENTRY		mode_sense
				ENTRY		mode_sense2

				ENTRY		start_unit
				ENTRY		stop_unit
				ENTRY		set_512_mode

				ENTRY		notify_me

				ENTRY		set_disk_sw
				ENTRY		trash_volume
				ENTRY		save_dp
				ENTRY		restore_dp
				ENTRY		set_our_dp
				ENTRY		check_532_rw
				ENTRY		munge_532
				ENTRY		divide

				PRINT		OFF

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

				EJECT
;____Mem_Manager_____
;*******************************************************
;
;	This routine is used to Get a User ID from the
;	memory manager for the SCSI Driver.  This is
;	required to get a userID for access reference.
;
;	Inputs:		None.
;
;	Outputs:	SCSID_uID	=	userID.
;				Registers	=	Scrambled
;
;	Errors:		$0207	iderr	Invalid userID.
;
;*******************************************************

			EXPORT	get_mm_id
get_mm_id	PROC
								;
								; Get userID.
								;
			pushword	#$0000

			pushword	#scsi_duid

			_GetNewID
			pla
			sta		|master_uid
			sta		|scsi_uid
@rts		rts
			
			ENDP

			EJECT

;*******************************************************
;
;	This routine is used to get the SCSI Manager Number
;	from the Supervisory Dispatcher at startup time.
;
;	Inputs:		None.
;
;	Outputs:	scsi_mgrnum		=	SCSI Manager Number
;				All Registers	=	scrambled
;
;	Errors:		None.
;
;*******************************************************

			EXPORT	get_scsimgr
get_scsimgr	PROC
								;
								; Get userID.
								;
			lda		#$0000		; Supervisor Driver Number (Unknown at this time)
			tax					; Supervisor Call Number ($0000)
			ldy		#$0002		; Spervisor ID for SCSI Manager ($0002)
			jsr		|rout2_s_disp
			stx		|scsi_mgrnum	; Preserve SCSI Driver Number for later use.
			rts
			
			ENDP

			EJECT

;*******************************************************
;
;	'zero_mem'
;
;	This routine will fill a section of memory with
;	$00s.
;
;	Inputs:		Acc			=	Buffer Size
;				X			=	Low Byte of new RAM Address
;				Y			=	High Byte of new RAM Address
;
;	Outputs:	scsi_zp0	=	Long Pointer to new RAM
;								Address
;				Acc			=	Scrambled
;
;	Errors:		None.
;
;*******************************************************

				EXPORT	zero_mem
zero_mem		PROC
										;
										; Save the buffer size.
										;
				sta		@buff_size
										;
										; Setup MOVE_INFO call for a
										; non-incrementing source and an
										; incrementing destination.
										;
				pushlong	#null_buff	;Source
				phy						;Destination High Word
				phx						;Destination Low Word
										;
										; Restore buffer size.  If = 0 then
										; we are to do an entire bank or at
										; least 64k bytes.  This may cross
										; banks.
										;
				lda		@buff_size
				beq		@do_64k
				pea		$0000
				pha
				bra		@move_type

@do_64k			pea		$0001
				pea		$0000

@move_type		pea		#move_scon_dinc
				jsl		move_info

@rts			rts
										;
										; Data for this call
										;
@buff_size		dc.w	null
null_buff		dc.w	null

				ENDP

				EJECT

;*******************************************************
;
;	This routine is used to request a section of RAM from
;	the memory manager for use by the driver.  This
;	routine allocates space for as requested by the byte
;	count in the Acc.
;
;	Inputs:		Acc			=	Number of bytes requested.
;								0 = 1 bank
;
;				Y			=	Offset to where the Handle
;								should be stored within
;								the allocated structure.
;
;	Outputs:	X			=	Low Word of new RAM Address
;				Y			=	High Word of new RAM Address
;				scsi_zp0	=	Long Pointer to new RAM
;								Address
;				Acc			=	Scrambled
;
;	Errors:		Not enough memory if carry set.
;
;*******************************************************

				EXPORT	get_space
get_space		PROC
											;
											; Request Acc bytes of RAM from
											; Memory Manager.
											;
				sty		|hndl_offset

				pushlong #00000000			;Space for result handle

				cmp		$0000				;Is it Null?
				beq		@null				;Yes. Request 1 bank

				pushword #0000				;Size in bytes of requested memory.
				bra		@stuff_a

@null			pushword #0001				;Request 1 bank

@stuff_a		sta		|buff_len
				pha
			
				pushword scsi_uid			;Our User ID
			
											;Attributes of requested mem.
				pea		attrfixed++\
						attrnospec++\
						attrpage

				pushlong #00000000			;Location Pointer.  Unused.

				_newhandle

				plx							; Get handle
				ply

				bcs		@rts				; Exit if an error.

											;
											; Save Handle for later
											;
				phy
				phx
											;
											; DEREF the Handle and get a
											; pointer for the new RAM.
											;
				stx		<scsi_zp0
				sty		<scsi_zp0+2
				ldy		#$0002
				lda		[scsi_zp0]
				tax
				lda		[scsi_zp0],y
				tay
											;
											; Stuff pointer for indirect access.
											;
				stx		<scsi_zp0
				sty		<scsi_zp0+2
											;
											; Call routine to zero out the
											; new memory
											;
				lda		|buff_len
				jsr		zero_mem
											;
											; Stuff Handle in RAM. Low byte first
											;
				ldy		|hndl_offset
				pullword [scsi_zp0],y
				iny
				iny
				pullword [scsi_zp0],y
											;
											; Restore X and Y to pointer status
											;
				ldx		<scsi_zp0
				ldy		<scsi_zp0+2
											;
											; Clean Exit.
											;
				clc
@rts			rts

				ENDP

				EJECT

;*******************************************************
;
;	This routine is used to request a section of RAM from
;	the memory manager for use by the driver.  This
;	routine allocates space for the get_devices call to
;	the SCSI Manager.
;
;	Inputs:		None.
;
;	Outputs:	dvc_list	=	Long Pointer to device List
;				next_dib	=	Long Pointer to First DIB
;				first_dib	=	Long Pointer to the beginning
;								of the RAM Allocated for the
;								new DIBs.
;				Acc			=	Scrambled
;
;	Errors:		Not enough memory if carry set.
;
;*******************************************************

				EXPORT	get_dvc_ram
get_dvc_ram		PROC
											;
											; Request the maximum device list
											; size bytes of RAM from GS/OS
											; System Service calls.
											;
				lda		#max_gdvc_buf+4		; Allocate space for list.
				ldy		#$0000

				ldx		|direct_page		;If this is the first time, then
				bne		@use_ours
				phx							;we don't want a DP setting of
											;NULL
											;
											; Save away GS/OS Direct Page values
											; until we have our own direct Page.
											;
				jsr		save_dp

				lda		#max_gdvc_buf+4		; Allocate space for list.
				ldy		#$0000

				ldx		|gsos_dpage
				stx		|direct_page

				jsr		|get_space

				pla							;Restore our settings preserving X & Y
				sta		|direct_page
				sta		|exit_dpage
				bra		@over_use_ours

@use_ours		jsr		|get_space
											;
											; Store newly allocated buffer
											; pointer into the parm list for
											; the get devices call.  Account
											; for the first 4 bytes that
											; contain the handle associated
											; with this memory segment.
											;
@over_use_ours	bcs		@rts_long
				clc
				txa
				sta		|dvc_ram
				adc		#$0004
				sta		|dvc_buff
				sta		<dvc_list
				tya
				sta		|dvc_ram+2
				adc		#$0000
				sta		|dvc_buff+2
				sta		<dvc_list+2
											;
											; Setup Pointer to our parm list
											; in GS/OS Direct Page.
											;
				ldx		|gsos_dpage
				lda		#get_dev_lst
				sta		>smgr_pl_ptr,x
				lda		#^get_dev_lst
				sta		>smgr_pl_ptr+2,x
											;
											; Load registers with their
											; required values.
											;
				lda		|scsi_mgrnum
				ldx		#cmd_get_dvc
				jsr		|rout2_s_disp
@rts_long		bcs		@rts
											;
											; Restore our direct page Device List
											;
				lda		|dvc_buff
				sta		<dvc_list
				lda		|dvc_buff+2
				sta		<dvc_list+2
											;
											; First word in [dvc_list}] contains
											; the address of a 1 page area that
											; we will be using for our direct
											; page.  Get it and move the
											; 'dvc_list' pointer to our direct
											; page area, but only if we have not
											; yet done it.
											;
				lda		|direct_page
				bne		@we_did_it			;It's already been done.

				lda		[dvc_list]
				tax
				lda		<dvc_list
				sta		>dvc_list,x
				lda		<dvc_list+2
				sta		>dvc_list+2,x
				txa
				sta		|direct_page
				sta		|exit_dpage
											;
											; Undocumented feature.  Store the SCSI
											; Managers Direct Page in the Default DIB
											; for external Access.
											;
				clc
				adc		#$0100
				sta		>default_dib+dib.handle
											;
											; Set our Direct Page with the first
											; 'x' Bytes equal to the GS/OS DP
											; settings.
											;
											;
				clc							; Calculate Source Address of GS/OS
				tdc							; Direct Page that we want.
				sta		|gsos_dpage			; Preserving GS/OS DP for later use.
				adc		#dev_num
				pea		$0000
				pha
											;
				clc							; Calculate Destination Address of
				lda		|direct_page		; our Direct Page that we want.
				adc		#dev_num
				pea		$0000
				pha
				
				pushlong	#dib_ptr+4		;Length of the move
				
				pushword	#move_sinc_dinc

				jsl		move_info			;Move the data
											;
											; Restore the Direct Page that we
											; borrowed until this was done.
											;
				jsr		restore_dp
											;
											; Set our Direct Page.
											;
				lda		|direct_page
				tcd
											;
											; Check for zero Devices.
											;
@we_did_it		ldy		#$0002
				lda		[dvc_list],y
				beq		@no_devices
											;
											; Ensure that device count is
											; in range.
											;
				cmp		#$0100+1			;No more than 256 devices
				blt		@count_ok
				lda		#max_dvc_cnt		;Max Count
@count_ok		sta		|dvc_count
				jsr		|make_dib
				stx		<next_dib
				stx		<first_dib
				sty		<next_dib+2
				sty		<first_dib+2

				lda		#no_error
				clc
											;
											; Restore the Direct Page that we
											; borrowed until this was done.
											;
@rts			pha
				php
				jsr		restore_dp
				plp
				pla
				rts
											;
											; No Devices Error.
											;
											; Restore the Direct Page that we
											; borrowed until this was done.
											;
@no_devices		jsr		restore_dp
				lda		#drvr_no_dev
				sec
				rts

get_dev_lst		dc.w	scsi_dtype
				dc.l	notify_me
dvc_buff		dc.l	null

				ENDP

				EJECT

;*******************************************************
;
;	This routine is the inverse of the above routine.
;	It calls the memory manager to deallocate the space
;	we used to request the device list from the SCSI
;	manager.
;
;	Inputs:		X			=	Low byte of Buffer pointer
;				Y			=	High byte of Buffer pointer
;
;	Outputs:	All Regs	=	Scrambled
;
;	Errors:		Not enough memory if carry set.
;
;*******************************************************

				EXPORT	fre_dvc_ram
fre_dvc_ram		PROC
										;
										; Remove the device list from
										; active use and free the mem.
										;
										; Stuff pointer for indirect access.
										;
				lda		|dvc_ram
				sta		<scsi_zp0
				lda		|dvc_ram+2
				sta		<scsi_zp0+2

				ldy		#$0002			;High byte first on stack
				pushword [scsi_zp0],y
				pushword [scsi_zp0]

				_disposehandle					

				rts

				ENDP

				EJECT

;____DIB_Mem_Mgr_____
;*******************************************************
;
;	This routine is used to request a section of RAM from
;	the memory manager for use by the driver.  This
;	routine allocates space for 1 DIB.
;
;	Inputs:		Acc			=	$00xx = DIB Count.
;								Null = $0100
;
;	Outputs:	X			=	Low Byte of new RAM Address
;				Y			=	High Byte of new RAM Address
;				scsi_zp0	=	Long Pointer to new RAM
;								Address
;				Acc			=	Scrambled
;
;	Errors:		Not enough memory if carry set.
;
;*******************************************************

				EXPORT	make_dib
make_dib		PROC
										;
										; Request $100 bytes of RAM for
										; each DIB from GS/OS System
										; Service calls.
										;
				and		#$00ff
				sta		|ram_page_cnt
				xba						;Cheap multiply by $0100
				ldy		#dib.handle		;Offset to where we want the handle
				jsr		|get_space		;Allocate space for DIBs.
				bcc		@rts
				stz		|ram_page_cnt
@rts			rts
			
				ENDP

				EJECT

;*******************************************************
;
;	This routine is called via a 'jsl' and returns the
;	page number of the caller in X and Y with X being
;	the low byte.  This is intended for use by the
;	completion routines in the DIB as a tool to find
;	the DIB's starting location in memory
;
;	Inputs:		None.
;
;	Outputs:	X			=	Low byte of callers page
;				Y			=	High byte of callers page
;				Acc			=	Scrambled
;
;	Errors:		None.
;
;*******************************************************

				EXPORT	g_dib_ptr
g_dib_ptr		PROC
									;
									; Get the callers return
									; address from the stack,
									; strip down to a page number,
									; transfer to X and Y and
									; return to caller via an 'rtl'
									;
				lda		<1,s
				sec
				sbc		#dib.complet+3-dib.linkptr
				tax
				lda		<3,s
				and		#$00ff
				tay
				rtl

				ENDP

				EJECT

;*******************************************************
;
;	'ADD_DIB_RAM'
;
;	This routine searches for the last DIB for this
;	driver, allocates enough ram for 4 additional DIBs
;	and links them in.
;
;	Inputs:		dib_ptr			=	Last DIB in Device link
;				add_dib_here	=	Where the new DIB is to be built
;				Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Outputs:	Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Errors:		Carry set if not enough memory
;
;*******************************************************

				EXPORT	add_dib_ram
add_dib_ram		PROC
											;
											; 'ADD_DIB_HERE' Pointer now points
											; to the last DIB currently allocated
											; and preserve location of last DIB.
											;
				lda		<add_dib_here
				sta		<last_dib
				pha							;Save and later restore
				lda		<add_dib_here+2
				sta		<last_dib+2
				pha							;Save and later restore
											;
											; Get some RAM for a few more DIBs..
											;
				lda		#dibs_2_make
				jsr		make_dib
				bcc		@over_0
											;
											; Got an Error. Fix the Stack and Exit.
											;
				tax

				pla							;Saved, and now restore
				sta		<last_dib+2
				pla							;Saved, and now restore
				sta		<last_dib

				txa

				rts
											;
											; Do First link
											;
@over_0			sty		<first_dib+2
				stx		<first_dib
											;
											; Dibicise first new DIB in the new
											; Memory.
											;
				sec							;Don't do the links.
				jsr		dibicise_new_ram
											;
											; Do the Head and Forward links also.
											;
				lda		#null
				ldy		#dib.headlnk
				sta		[first_dib],y

				ldy		#dib.fdvclnk
				sta		[first_dib],y

				ldy		#dib.unitnum		;Don't forget the unit number
				sta		[first_dib],y
											;
											; Finalise the new DIB Structure.
											;
				dec		|main_caller
											;********
											;******** 6/12/89  Begin fix MSG
											;******** rebuild flag being destroyed.
											;********
				lda		|rebuild			;The state of this flag must be preserved.
				pha
				stz		|rebuild			;Set to zero.  (Full Rebuild)

				jsr		set_link_ptrs		;Now we do the links.

				pla
				sta		|rebuild			;Restore the callers state for this flag.

;				stz		|rebuild			;Set to zero.  (Full Rebuild)

;				jsr		set_link_ptrs		;Now we do the links.
											;********
											;******** 6/12/89  End fix MSG
											;******** rebuild flag being destroyed.
											;********
											;
											; Finish the last three links.  They
											; live in the new memory segment.
											;
				lda		#dibs_2_make-1
				sta		@loop_cnt

@link_loop		clc

				lda		<next_dib
				sta		<last_dib
				adc		#dib_size
				sta		<next_dib

				lda		<next_dib+2
				sta		<last_dib+2
				adc		#^dib_size
				sta		<next_dib+2

				dec		|main_caller
											;********
											;******** 6/12/89  Begin fix MSG
											;******** rebuild flag being destroyed.
											;********
				lda		|rebuild			;The state of this flag must be preserved.
				pha
				stz		|rebuild			;Set to zero.  (Full Rebuild)

				jsr		dibicise_new_dib

				pla
				sta		|rebuild			;Restore the callers state for this flag.

;				stz		|rebuild

;				jsr		dibicise_new_dib
											;********
											;******** 6/12/89  End fix MSG
											;******** rebuild flag being destroyed.
											;********
				dec		@loop_cnt
				bne		@link_loop
											;
											; Clean Exit
											;
											; Reinitialise mem_dib_cnt to zero.
											;
				lda		#null
				ldy		#dib.mem_dib_cnt
				sta		[first_dib],y
											;
											; Wipe out last link pointer to no where.
											;
				sta		[next_dib]
				ldy		#dib.linkptr+2
				sta		[next_dib],y

				pla							;Saved, and now restore
				sta		<last_dib+2
				pla							;Saved, and now restore
				sta		<last_dib

				clc
				rts
											;
											; Data Area.
											;
@loop_cnt		dc.w	null

				ENDP

				EJECT

;*******************************************************
;
;	FIND_EMPTY
;
;	This routine serches forward from the current DIB
;	looking for an empty.  If it exits with the carry
;	clear, then one has been found.  If the carry is
;	set, then it points to the last DIB in the linked
;	list and found non empty.
;
;	Inputs:		<DIB_PTR		=	Last DIB Location
;				Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Outputs:	<ADD_DIB_HERE	=	Empty DIB if found
;				Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Errors:		Carry set if no empties were found
;
;*******************************************************

				EXPORT	find_empty
find_empty		PROC
										;
										; Are we at the last DIB? Set beginning
										; pointer for check.
										;
				lda		<dib_ptr
				sta		<last_dib
				sta		<add_dib_here
				lda		<dib_ptr+2
				sta		<last_dib+2
				sta		<add_dib_here+2
										;
										; Are we at the last DIB? (LOOP)
										;
@fnd_empty_lup	ldy		#$0002
				lda		[add_dib_here]
				tax

				lda		[add_dib_here],y;Should never be in Bank $00
				bne		@advance_ptr	;No.

@sec			sec						;Yes. Exit
				rts
										;
										; Advance Ptr to next DIB.
										;
@advance_ptr	sta		<add_dib_here+2
				stx		<add_dib_here
										;
										; Is it Free?
										;
										; Must have a zero Head Link Ptr,
										; Forward Device Link Ptr, and
										; DIB Device Number to be considered
										; free.
										;
				ldy		#dib.devnum
				lda		[add_dib_here],y
				bne		@fnd_empty_lup	;No.  Check next one.

				ldy		#dib.headlnk
				lda		[add_dib_here],y
				bne		@fnd_empty_lup	;No.  Check next one.

				ldy		#dib.fdvclnk
				lda		[add_dib_here],y
				bne		@fnd_empty_lup	;No.  Check next one.
										;
										; Make the DIB Device Number non-zero
										; to prvent it from being selected
										; again before it is started up by
										; the operating system.
										;
				ldy		#dib.devnum
				lda		[add_dib_here],y
				inc		a
				sta		[add_dib_here],y

				clc						;Yes. Exit
				rts

				ENDP

				EJECT
			
;*******************************************************
;
;	'chk_ram'
;
;	This routine checks to see if we have any room left
;	in this memory segment for another DIB.  If not then
;	the memory manager is called and the new RAM is
;	dibicised.
;		
;	Inputs:		Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Carry		=	1
;					Acc		=	Error
;				Carry		=	0
;					Acc		=	ram_page_cnt
;							=	0	no more devices
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		None.
;
;*******************************************************
										;
										; Start of the code segment for this.
										;
				EXPORT	chk_ram
chk_ram			PROC
										;
										; All done with the DIB Update now
										; do we have any more RAM left to
										; build further DIBs?
										;
				dec		|ram_page_cnt
				bne		@adv_ptrs		;Yes we do.  Continue on.

										;
										; Account for 'part_cnt' being equal
										; to the Actual Partition Count - 1
										;
				sec
;				clc						;Bug Fix 4/3/89

				lda		|dvc_count		;No we don't.  Get the remaining device
				adc		|part_cnt		;count and add the remaining Part count
;				dec		a				;Subtract our current working device
;				dec		a				;Subtract our current working DIB
				beq		@exit			;Are we done? Yes.
				bmi		@exit			;Yes.
				cmp		#$00ff+1		;No. Bound check the result and
				blt		@call_make_dib

@set_max		lda		#$00ff			;Set to max if greater.
@call_make_dib	jsr		make_dib		;Call routine to make the DIB RAM
				bcc		@update_new		;available.  ERROR?
				cmp		#drvr_no_dev	;Yes.  Then Exit
				bne		@sec
				clc
				rts
@sec			sec
				rts
										;
										; We had room in the current RAM
										; Space so we will advance all the
										; pointers to their next values
										; within that space.
										;
@adv_ptrs		clc
				lda		<next_dib
				sta		<last_dib
				adc		#dib_size
				sta		<next_dib
				lda		<next_dib+2
				sta		<last_dib+2
				adc		#^dib_size
				sta		<next_dib+2
				jsr		dibicise_new_dib
				bra		@exit
											;
											; We have a new memory segment so
											; we need to account for this when
											; we advance our pointers.
											;
@update_new		stx		<first_dib
				sty		<first_dib+2
				lda		<next_dib
				sta		<last_dib
				lda		<next_dib+2
				sta		<last_dib+2
				clc							;Do Links Also
				jsr		dibicise_new_ram
											;
											; Clean exit.
											;
@exit			lda		|ram_page_cnt
				clc
				rts

				ENDP

				EJECT

;____DIB_Building____
;*******************************************************
;
;	REBLD_DIBS
;
;	It is the function of this routine to blindly
;	rebuild the DIBs for the physical device attached to
;	the current DIB.  If that device is non-partitioned,
;	then only one DIB will be built, and a DISK SWITCHED
;	will be issued.  If there are more than one device,
;	as in partitioned media, then all the DIBs for the
;	partitions will be re-built.  This is treated as if
;	the device just came online for the first time.  If
;	the new device has fewer partitions than there are
;	DIBs that are already allocated, then the left over
;	will be marked as being offline.  If, on the other
;	hand, there are more partitions than DIBs, then new
;	ones will be allocated and built.  These will then be
;	permanantly linked to that physical device.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if operation could not be
;				completed.  Out of memory for example.
;
;*******************************************************
										;
										; Start of the code segment for this.
										;
				EXPORT	rebld_dibs
rebld_dibs		PROC
										;
										; Clear Linked Device Flag.  This will
										; account for the situation where a
										; partitioned disk has been reduced to
										; a single volume.
										;
				stz		@linked
										;
										; Zero the count of new devices to be
										; started.
										;
				stz		|new_dib_cnt
										;
										; Point to first DIB in link if any.
										; If it is zero, then we are already
										; there.
										;
				ldy		#dib.headptr+2
				lda		[dib_ptr],y
				ldy		#dib.headptr
				ora		[dib_ptr],y
				beq		@over
										;
										; It's non-zero.  Set up DIB_PTR
										;
				lda		[dib_ptr],y
				tax
				ldy		#dib.headptr+2
				lda		[dib_ptr],y
				sta		<dib_ptr+2
				stx		<dib_ptr
										;
										; Set Temp Pointer to first DIB in the link.
										;
@over			lda		<dib_ptr
				sta		<scsi_zp6
				lda		<dib_ptr+2
				sta		<scsi_zp6+2 
										;
										; Mark each DIB in the link as offline
										; and switched.
										;
@loop			ldy		#dib.dvcflag
				lda		[scsi_zp6],y
				and		#(dvc_online++\
						pdos_part)--\
						$ffff
				ora		#dvc_switch++\
						dvc_hardofl
				sta		[scsi_zp6],y
										;
										; Mark each DIB in the link as Unlinked.
										;
				ldy		#dib.dvcchar
				lda		[scsi_zp6],y
				and		#linked_dvc--\
						$ffff
				sta		[scsi_zp6],y
										;
										; Zero Partition Offset for each DIB
										;
				lda		#null
				ldy		#dib.start_blk
				sta		[scsi_zp6],y
				ldy		#dib.start_blk+2
				sta		[scsi_zp6],y
										;
										; It's non-zero.  Set up next DIB_PTR
										;
				clc
				ldy		#dib.fdvcptr
				lda		[scsi_zp6],y
				tax
				beq		@null

				sec

@null			ldy		#dib.fdvcptr+2
				lda		[scsi_zp6],y
				bne		@doit
				bcc		@over_0

@doit			sta		<scsi_zp6+2
				stx		<scsi_zp6

				bra		@loop
										;
										; Initialize a few values.
										;
@over_0			lda		#$ffff			;The following values will be incrimented
				sta		|first_time		;with zero indicating special action.
				sta		|rebuild		;This is set to $ffff so that it will only
				sta		|only_one		;occur on the first pass through.

				stz		|part_cnt
				stz		|rpm_blk_num
				stz		|new_dib
										;
										; MAIN LOOP OF CODE SEGMENT.
										;
@main_loop		jsr		read_pm_blk
				bcc		@with_data		;This device is with data
				lda		auto_sense_data+\		;Is it an audio disk?
						rqst_sens.addnl_sens_code
				and		#$00fe			;Checking for $84, $64, or $63
				cmp		#$0064			;This cover $63, $64
				beq		@single
				cmp		#$0084
				beq		@single
				sec
				rts						;There was an error!
										;
										; Valid Partition Map?
										;
@with_data		jsr		chk_map
				bcs		@done
				bne		@main
										;
										; We have a vaild Partition Map so
										; we need to make an entry for each
										; partition other than those with
										; the partition types of 'MAP',
										; 'SCRATCH', and 'FREE'.  All others
										; will be considered valid.  We also
										; only accept up to 'max_partitions'
										; partitions per disk.
										;
										; How many partitions are there?
										;
				lda		|pm.MapBlkCnt\
						+internal_buff
				bne		@force_max
				lda		|pm.MapBlkCnt+2\
						+internal_buff
				xba						;Value is High --> Low
				cmp		#max_partitions+1
				blt		@cnt_ok
@force_max		lda		#max_partitions
@cnt_ok			sta		|part_cnt

				cmp		#$0002+1
				blt		@non_linked
				dec		@linked
@non_linked		dec		|part_cnt
				bpl		@main
										;
										; No valid entry.  Set as if no
										; partitions.
										;
@single			jsr		no_partition	;Marks DIB as online and switched also.
				jsr		trash_volume	;Trash the volume if caller wishes.
										;See |trash_it flag in trash_volume
				lda		#null
				clc
				rts
										;
										; Check validity of partiton map.
										;
@main			jsr		check_map_entry
				bcc		@chk_pdos
										;
										; It was undefined or unusable
										;
										; Is the Pointer good?
										;
										; Is it a cold DIB?
										;
				ldy		#dib.dvcflag
				lda		[add_dib_here],y
				and		#cold_dib
				beq		@over_cold		;No.  Skip clear code.
										;
										; Mark DIB to the Default.
										;
				ldy		#dib.dvcflag
				lda		#wait_mode++\	; Wait Mode is default
						cold_dib		; and they start cold.
				sta		[add_dib_here],y
										;
										; Must set Head Link Ptr, Forward
										; Device Link Ptr, and DIB Device
										; Number to Null.
										;
				lda		#null

				ldy		#dib.devnum
				sta		[add_dib_here],y

				ldy		#dib.headlnk
				sta		[add_dib_here],y

				ldy		#dib.fdvclnk
				sta		[add_dib_here],y
										;
										; And skip it.
										; Is there more to do?
										;
@over_cold		dec		|part_cnt
				bmi		@do_dpi			;No!
				jmp		@main_loop		;Yes!
										;
										; Check to see if the post driver install
										; list contains any entries.
										;
@do_dpi			jsr		do_post_install		
										;
										; Check the only one flag.  If true
										; then unlink it.
										;
				lda		|only_one
				bne		@done

				ldy		#dib.dvcchar
				lda		[rebuild_zp],y
				and		#linked_dvc--\
						$ffff
				sta		[rebuild_zp],y
										;
										; Clean exit.
										;
@done

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


				IF			scsi_dtype = mcd_40\
				OR			scsi_dtype = direct_acc		THEN

				jsr		mode_sense

				lda		|internal_buff+1
				bpl		@write_enable

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

@write_enable	ldy		#dib.dvcchar
				lda		[dib_ptr],y
				ora		#write_allow

@wp_chk			sta		[dib_ptr],y
				lda		#$0000

				ENDIF

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

				clc
@rts			rts
										;
										; Check the Overflow bit.  If it is set,
										; then this is a ProDOS Partition and we
										; need to set the correct bit in the DIB.
										;
@chk_pdos		bvc		@over_1
										;
										; Set the ProDOS Bit.
										;
				ldy		#dib.dvcflag
				lda		[dib_ptr],y
				ora		#pdos_part
				sta		[dib_ptr],y
										;
										; Issue call to fix unit 1.  This will
										; guarantee that the first unit is also
										; the first ProDOS Partition on the disk.
										;
				jsr		fix_unit_1
										;
										; Get the Block Count for this
										; Partition and store it in the
										; DIB.  The count as it exists
										; in the partition data is stored
										; High byte to Low byte.  We will
										; need to alter this to Low --> High
										; format.
										;
@over_1			ldy		#dib.blkcnt
				lda		|pm.PartBlkCnt+2\
						+internal_buff
				xba
				sta		[dib_ptr],y
				ldy		#dib.blkcnt+2
				lda		|pm.PartBlkCnt\
						+internal_buff
				xba
				sta		[dib_ptr],y
										;
										; Now we need to get the starting
										; location for this partition.  This
										; will be added to the requested
										; block to generate a real block
										; address.  Also in High --> Low format.
										;
				ldy		#dib.start_blk+2
				lda		|pm.PyPartStart+2\
						+internal_buff
				sta		[dib_ptr],y
				ldy		#dib.start_blk
				lda		|pm.PyPartStart\
						+internal_buff
				sta		[dib_ptr],y
										;
										; Get Location of this partition on disk.
										;
				ldy		#dib.part_blk
				lda		|rpm_blk_num
				sta		[dib_ptr],y
										;
										; Mark DIB as online and switched.
										;
				ldy		#dib.dvcflag
				lda		[dib_ptr],y
				and		#dvc_hardofl--\
						$ffff
				ora		#dvc_switch++\
						dvc_online
				sta		[dib_ptr],y

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

				IF			scsi_dtype = direct_acc	THEN

										;
										; Set the Write Allow/Read Allow
										; Mask byte to allow all bits to pass.
										;
										; This is used to filter or strip out the
										; Write Enable bit in the case where the
										; DIB Status says that the partition is
										; write enabled but the disk is physically
										; write protected.  This can happen with a
										; Partitioned Syquest Drive.
										;
				lda		#$ffff
				sta		@mask_flag
										;
										; Is the device Removable?
										;
				ldy		#dib.dvcchar

				lda		[dib_ptr],y
				and		#removable
				beq		@non_remove		;No.
										;
										; Issue the MODE SENSE Call to see if
										; Write Protected.
										;
				jsr		mode_sense2		;Issue call to use Sense Buffer
				bcs		@non_remove
										;
										; Check the Write Protected Bit.
										;
				lda		|sense_data+\
						mode.w_protect-1		;But bit in Hi Byte
				bpl		@non_remove
										;
										; Set the Write Allow/Read Allow
										; Mask byte to filter the Write Allow out.
										;
				lda		#write_allow--\
						$ffff
				sta		@mask_flag
										;
										; Set the Characteristics to Match the
										; Read and Write Enables in the Partition
										; Status Field.
										;
@non_remove		lda		|pm.PartStatus+2\
						+internal_buff
				xba
				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
				and		@mask_flag
				sta		[dib_ptr],y

				ENDIF

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

										;
										; Mark DIB as Linked.
										;
				lda		@linked
				bpl		@skip_link
				ldy		#dib.dvcchar
				lda		[dib_ptr],y
				ora		#linked_dvc
				sta		[dib_ptr],y
										;
										; Inc the ONLY_ONE Flag.  If it is
										; null then save the dib_ptr.  On
										; exit we will check this flag and
										; if = 0 we will use this pointer
										; to clear the linked flag.
										;
				inc		|only_one
				bne		@skip_link

				lda		<dib_ptr
				sta		<rebuild_zp
				lda		<dib_ptr+2
				sta		<rebuild_zp+2
										;
										; Wipe out block 2 of partition or
										; volume
										;
@skip_link		jsr		trash_volume
										;
										; Is this a new dib?
										;
				bit		|new_dib
				bpl		@set_dsw		;No.  Set disk switch

				ldx		<dib_ptr
				ldy		<dib_ptr+2
				jsr		add_dib_ptr		;Yes. Add to the list for post driver
				bra		@over_dsw		; install.
										;
										; Set the DISK SWITCHED
										;
@set_dsw
;				jsr		set_disk_sw
										;
										; Is there more to do?
										;
@over_dsw		stz		|new_dib
				dec		|part_cnt
				bpl		@over_do_dpi	;Yes!
				jmp		@do_dpi			;No!
										;
										; All done with the DIB Update now
										; do we have any more DIBs, for this
										; linked device?
										;
@over_do_dpi	ldy		#dib.fdvcptr
				lda		[dib_ptr],y
				tax

				ldy		#dib.fdvcptr+2
				ora		[dib_ptr],y
				beq		@over_2
				lda		[dib_ptr],y

				sta		<dib_ptr+2
				stx		<dib_ptr
										;
										; Do the next one.
										;
@jmp_main		jmp		@main_loop
										;
										; It's zero.  Need to add DIBs
										;
										; Is there an empty DIB some where?
										;
@over_2			jsr		find_empty
				bcc		@found_empty
										;
										; Get room for some more DIBs.
										;
				jsr		add_dib_ram
				bcc		@over_2
				

				rts
										;
										; We need to set some pointers for
										; the routines that we will be
										; calling.  These pointers are similar
										; to those used in the startup code
										;
										; 1st.  Set the pointer to the next
										; DIB to be build.
										;
@found_empty	lda		<add_dib_here
				sta		<dib_ptr
				sta		<next_dib
				lda		<add_dib_here+2
				sta		<dib_ptr+2
				sta		<next_dib+2
										;
										; Preserve the Link Pointer.  Otherwise
										; it will be destroyed by the dibicise
										; routine.
										;
				ldy		#dib.linkptr+2
				lda		[next_dib]
				pha
				lda		[next_dib],y
				pha
										;
										; Preserve the Handle for this RAM Segment.
										; Otherwise it will be destroyed by the
										; dibicise routine.
										;
				ldy		#dib.handle+2
				lda		[next_dib],y
				pha
				ldy		#dib.handle
				lda		[next_dib],y
				pha
										;
										; Preserve the current Device Flags.
										;
				ldy		#dib.dvcflag
				lda		[next_dib],y
				pha
										;
										; Set the 'first_dib' pointer to the
										; first dib in the same memory segment
										; from the memory manager as the one
										; that contains the space to be used
										; for this new dib.  first we get the
										; handle.
										;
				ldy		#dib.handle
				lda		[next_dib],y
				sta		<first_dib

				ldy		#dib.handle+2
				lda		[next_dib],y
				sta		<first_dib+2
										;
										; then we deref the handle.
										;
				ldy		#$0002
				lda		[first_dib]
				tax
				lda		[first_dib],y
				sta		<first_dib+2
				stx		<first_dib
										;
										; Tell the dibicize routine that this
										; is not from the startup code.  This
										; also forces the descriptive names
										; minor id to be changed leaving the
										; major or device portion as it is.
										;
				dec		|main_caller
										;
										; Dibicise the new dib.
										;
				jsr		dibicise_new_dib
										;
										; Restore the current Device Flags.
										;
				pla
				ldy		#dib.dvcflag
				sta		[next_dib],y
										;
										; Restore the Handle for this RAM.
										; Otherwise it is destroyed by the
										; dibicise routine.
										;
				pla
				ldy		#dib.handle
				sta		[next_dib],y
				pla
				ldy		#dib.handle+2
				sta		[next_dib],y
										;
										; Restore the Link Pointer.  Otherwise
										; it is destroyed by the dibicise
										; routine.
										;
				pla
				ldy		#dib.linkptr+2
				sta		[next_dib],y
				pla
				sta		[next_dib]
										;
										; Restore the DIB Extension Pointer.
										; Otherwise it is destroyed by the
										; dibicise routine.
										;
				clc						;we are going to add
				lda		<next_dib		;get low word of this dib

				adc		#dib.start_blk	;adc offset to ext dib ptr
				ldy		#dib.ext_ptr	;get offset to where this goes
				sta		[next_dib],y	;and store low word of ptr

				lda		<next_dib+2		;get low word of this dib
				adc		#^dib.start_blk	;adc high offset to ext ptr
				ldy		#dib.ext_ptr+2	;get offset to where this goes
				sta		[next_dib],y	;and store the high word ptr
										;
										; Rev the unit number to be unique
										;
				ldy		#dib.unitnum
				lda		[next_dib],y
				tax
				and		#max_p_mask
				cmp		#max_p_mask
				beq		@all_done

				txa
				clc
				adc		#p_mask_adder
				sta		[next_dib],y
										;
										; This flag indicates that this is a
										; new dib and that the routine that
										; finalizes this dib will have to
										; issue a post driver install call.
										;
				dec		|new_dib
										;
										; Go back to the main loop where this
										; dib will be finalized and installed.
										;
				jmp		@main_loop
										;
										; Clean Exit
										;
@all_done		clc
				rts
										;
										; Misc Data for this sub-routine
										;
@linked			dc.w		null
@part_num		dc.w		null
@vPart_cnt		dc.w		null
@temp			dc.w		null
@mask_flag		dc.w		null

				ENDP

				EJECT
			
;*******************************************************
;
;	'do_part_dib'
;
;	This routine issues a READ call to the device and
;	then uses the data returned to check for any
;	partitions on the media.  If partitions do exist, it
;	then creates the needed dibs from those that have
;	been allocated but unused, or if needed will create
;	a new memory segment and continue on.
;		
;	Inputs:		<next_dib	=	Next DIB start		(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	<next_dib	=	last DIB Built		(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		None.
;
;*******************************************************
										;
										; Start of the code segment for this.
										;
				EXPORT	do_part_dib
do_part_dib		PROC

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

				IF			part_suprt = true		THEN

										;
										; MAIN LOOP OF CODE SEGMENT.
										;
@main_loop		jsr		read_pm_blk
				bcc		@chk_map		;Was there an error?
				
				jsr		no_partition										;
				bra		@done
										; Valid Partition Map?
										;
@chk_map		jsr		chk_map
				bcs		@done
				bne		@main
										;
										; We have a vaild Partition Map so
										; we need to make an entry for each
										; partition other than those with
										; the partition types of 'MAP',
										; 'SCRATCH', and 'FREE'.  All others
										; will be considered valid.  We also
										; only accept up to 'max_partitions'
										; partitions per disk.
										;
										; How many partitions are there?
										;
				lda		|pm.MapBlkCnt\
						+internal_buff
				bne		@force_max
				lda		|pm.MapBlkCnt+2\
						+internal_buff
				xba						;Value is High --> Low
				cmp		#max_partitions+1
				blt		@cnt_ok
@force_max		lda		#max_partitions
@cnt_ok			sta		|part_cnt

				dec		|part_cnt
				bpl		@main
										;
										; No valid entry.  Set as if no
										; partitions.
										;
				jsr		no_partition
				clc
				rts
										;
										; Check validity of partiton map.
										;
@main			jsr		check_map_entry
				bcc		@chk_pdos
										;
										; It was undefined or unusable
										; Mark DIB to the Default.
										;
				ldy		#dib.dvcflag
				lda		#wait_mode++\	; Wait Mode is default
						cold_dib		; and they start cold.
				sta		[next_dib],y
										;
										; Must set Head Link Ptr, Forward
										; Device Link Ptr, and DIB Device
										; Number to Null.
										;
				lda		#null

				ldy		#dib.devnum
				sta		[next_dib],y

				ldy		#dib.headlnk
				sta		[next_dib],y

				ldy		#dib.fdvclnk
				sta		[next_dib],y
										;
										; And skip it.
										; Is there more to do?
										;
				dec		|part_cnt
				bpl		@main_loop
										;
										; Clean exit.
										;
@done			clc
@rts			rts
										;
										; Check the Overflow bit.  If it is set,
										; then this is a ProDOS Partition and we
										; need to set the correct bit in the DIB.
										;
@chk_pdos		bvc		@over
										;
										; Set the ProDOS Bit.
										;
				ldy		#dib.dvcflag
				lda		[next_dib],y
				ora		#pdos_part
				sta		[next_dib],y
										;
										; Issue call to fix unit 1.  This will
										; guarantee that the first unit is also
										; the first ProDOS Partition on the disk.
										;
				jsr		fix_unit_1
										;
										; Get the Block Count for this
										; Partition and store it in the
										; DIB.  The count as it exists
										; in the partition data is stored
										; High byte to Low byte.  We will
										; need to alter this to Low --> High
										; format.
										;
@over			ldy		#dib.blkcnt
				lda		|pm.PartBlkCnt+2\
						+internal_buff
				xba
				sta		[next_dib],y
				ldy		#dib.blkcnt+2
				lda		|pm.PartBlkCnt\
						+internal_buff
				xba
				sta		[next_dib],y
										;
										; Now we need to get the starting
										; location for this partition.  This
										; will be added to the requested
										; block to generate a real block
										; address.  Also in High --> Low format.
										;
				ldy		#dib.start_blk+2
				lda		|pm.PyPartStart+2\
						+internal_buff
				sta		[next_dib],y
				ldy		#dib.start_blk
				lda		|pm.PyPartStart\
						+internal_buff
				sta		[next_dib],y
										;
										; Get Location of this partition on disk.
										;
				ldy		#dib.part_blk
				lda		|rpm_blk_num
				sta		[next_dib],y
										;
										; Set this device to ONLINE.
										;
				ldy		#dib.dvcflag
				lda		[next_dib],y
				ora		#dvc_online
				sta		[next_dib],y

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

				IF			scsi_dtype = direct_acc	THEN

										;
										; Set the Characteristics to Match the
										; Read and Write Enables in the Partition
										; Status Field.
										;
				lda		|pm.PartStatus+2\
						+internal_buff
				xba
				and		#vconf_wr_enable++\
						vconf_rd_enable
				asl		a
				sta		@temp
				ldy		#dib.dvcchar
				lda		[next_dib],y
				and		#write_allow++\
						read_allow--\
						$ffff
				ora		@temp
				sta		[next_dib],y

				ENDIF

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

										;
										; Increment the partition count.
										;										
				inc		|part_num
				beq		@chk_ram
										;
										; Update the High seven bits of the
										; unit number checking for the max
										; value of 'max_partitions'.
										;
				ldy		#dib.unitnum
				lda		[next_dib],y
				tax
				and		#max_p_mask
				cmp		#max_p_mask
				beq		@all_done
				txa
				clc
				adc		#p_mask_adder
				sta		[next_dib],y
				inc		|vPart_cnt
										;
										; All done with the DIB Update now
										; do we have any more DIBs, and if
										; so, do we have any more RAM left
										; to build further DIBs?
										;
@chk_ram		dec		|part_cnt		
				bmi		@all_done
				dec		|main_caller
				jsr		chk_ram
				bcs		@bad_call		;ERROR?
				jmp		@main_loop		;No. Do the next one.
@bad_call		cmp		#drvr_no_dev	;Yes.  Then Exit
				bne		@sec
				clc
				rts
@sec			sec
				rts
										;
										; Clean Exit
										;
@all_done		clc
				rts
										;
										; Data Area
										;
@temp			dc.w	null

				ELSE

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

										;
										; Clean Exit
										;
				lda		#null
				clc
				rts

				ENDIF

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

				ENDP

				EJECT
			
;*******************************************************
;
;	'dibicise_new_ram'
;
;	This routine takes the DIB located at [last_dib] and
;	copies it into the new ram space.  It does this
;	while preserving the handle at offset 'dib.handle'.
;	By so doing, it will be possible to free this ram
;	space at some later time if that is deemed correct.
;		
;	Inputs:		<last_dib		=	Last DIB built		(LONG)
;				<first_dib		=	Pointer to new		(LONG)
;									ram space.
;				Acc				=	Unspecified
;				Carry	Clear	=	Do Link Pointers
;						Set		=	Skip Link Pointers
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Outputs:	<last_dib		=	unchanged			(LONG)
;				<first_dib		=	unchanged			(LONG)
;				<next_dib		=	<first_dib			(LONG)
;				|curr_hndl		=	Handle for new ram	(LONG)
;
;				Acc				=	Unspecified
;				Carry			=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;		If Carry Set on entry then
;
;				|tot_dib_cnt	=	$0000				(WORD)
;
;		If Carry Clear on entry then
;
;				[last_dib]		=	points to this DIB	(LONG)
;				[first_dib]		=	defaults set		(PTR)
;				'Int Pntrs'		=	Set to this DIB		(LONG)s
;				'dispname'		=	Device Name			(STR)
;				|tot_dib_cnt	=	# of valid DIBs		(WORD)
;
;	Errors:		None.
;
;*******************************************************

				EXPORT	dibicise_new_ram
dibicise_new_ram	PROC
											;
											; Save Do Links Flag.
											;
				php
											;
											; Preserve Handle for this
											; memory segment.
											;
				ldy		#dib.handle
				lda		[first_dib],y
				sta		|curr_hndl
				ldy		#dib.handle+2
				lda		[first_dib],y
				sta		|curr_hndl+2
											;
											; Using the built in move routine,
											; copy the last DIB built to the 
											; first_dib location for the next
											; dib to be built.
											;
				pushlong	<last_dib
				pushlong	<first_dib
				pushlong	#dib_size
				pushword	#move_sinc_dinc

				jsl		move_info			;Move the data
											;
											; Restore the handle to it's
											; origonal location.  This will
											; then be copied to every other
											; dib that is built in this
											; memory segment.
											;
				ldy		#dib.handle
				lda		|curr_hndl
				sta		[first_dib],y
				ldy		#dib.handle+2
				lda		|curr_hndl+2
				sta		[first_dib],y
											;
											; Set 'next_dib' pointer to the
											; first dib in this memory
											; segment.
											;
				lda		<first_dib
				sta		<next_dib
				lda		<first_dib+2
				sta		<next_dib+2

				ldy		#dib.dvcflag

;				lda		[first_dib],y
;				ora		#cold_dib			;New DIB.  Do Cold Start.
				lda		#wait_mode++\		;New DIB.  Do Cold Start and in Wait Mode.
						cold_dib

				sta		[first_dib],y
											;
											; Reinitialise mem_dib_cnt to zero.
											;
				lda		#null
				ldy		#dib.mem_dib_cnt
				sta		[first_dib],y
											;
											; Reinitialise DIB Dev Number also.
											;
				ldy		#dib.devnum
				sta		[first_dib],y
											;
											; Finalise the new DIB Structure?
											;
				plp
				bcc		set_link_ptrs
				rts

				ENDP

				EJECT
			
;*******************************************************
;
;	'dibicise_new_dib'
;
;	This routine takes the DIB located at [last_dib] and
;	copies it into the new ram space.
;		
;	Inputs:		<last_dib	=	Last DIB built		(LONG)
;				<next_dib	=	Next DIB start		(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	<last_dib	=	unchanged			(LONG)
;				<next_dib	=	unchanged			(LONG)
;				[last_dib]	=	points to this DIB	(LONG)
;				[next_dib]	=	defaults set		(PTR)
;				'Int Pntrs'	=	Set to this DIB		(LONG)s
;				'dispname'	=	Device Name			(STR)
;				|tot_dib_cnt=	# of valid DIBs		(WORD)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		None.
;
;*******************************************************

				EXPORT	dibicise_new_dib
dibicise_new_dib	PROC
											;
											; Using the built in move routine,
											; copy the last DIB built to the 
											; next_dib location for the next
											; dib to be built.
											;
				pushlong	<last_dib
				pushlong	<next_dib
				pushlong	#dib_size
				pushword	#move_sinc_dinc

				jsl		move_info			;Move the data

											;
											; Finalise the new DIB Structure.
											;
				EXPORT	set_link_ptrs
											;
											; Update Links and DIB ext ptrs
											;
set_link_ptrs	bit		|rebuild
				bmi		@rebuild_links

				clc							;we are going to add
				lda		<next_dib			;get low word of this dib
				sta		[last_dib]			;store in last dib (implied y = 0)

				adc		#dib.start_blk		;adc offset to ext dib ptr
				ldy		#dib.ext_ptr		;get offset to where this goes
				sta		[next_dib],y		;and store low word of ptr

				ldy		#dib.linkptr+2		;get offset to high word
				lda		<next_dib+2			;get the high word and
				sta		[last_dib],y		;store it in the last dib

				adc		#^dib.start_blk		;adc high offset to ext ptr
				ldy		#dib.ext_ptr+2		;get offset to where this goes
				sta		[next_dib],y		;and store the high word ptr
											;
											; Zero out the Link for this DIB
											;
				lda		#null
				sta		[next_dib]
				ldy		#dib.linkptr+2
				sta		[next_dib],y
											;
											; Zero out the Starting Block for this DIB
											;
@rebuild_links	lda		#null
				ldy		#dib.start_blk
				sta		[next_dib],y
				ldy		#dib.start_blk+2
				sta		[next_dib],y
											;
											; Increment the number of active DIBs in
											; this memory segement.
											;
				ldy		#dib.mem_dib_cnt
				lda		[first_dib],y
				inc		a
				sta		[first_dib],y
											;
											; In the DIB Extension as defined for this
											; driver is the root structure of the
											; pointers that are used to call the SCSI
											; Manager.  At this time we need to update
											; them so that they too point into this
											; dib and not the last one.
											;
				clc
				lda		<next_dib			;Low word of Completion Pointer
				adc		#dib.complet
				ldy		#dib.compvec
				sta		[next_dib],y

				lda		<next_dib+2			;High word of Completion Pointer
				adc		#^dib.complet
				ldy		#dib.compvec+2
				sta		[next_dib],y

				clc
				lda		<next_dib			;Low word of Command Packet Pointer
				adc		#dib.scsicmd
				ldy		#dib.cp_ptr
				sta		[next_dib],y

				lda		<next_dib+2			;High word of Command Packet Pointer
				adc		#^dib.scsicmd
				ldy		#dib.cp_ptr+2
				sta		[next_dib],y

				clc
				lda		<next_dib			;Low word of Send Structure Pointer
				adc		#dib.trx_buff
				ldy		#dib.trx_ptr
				sta		[next_dib],y

				lda		<next_dib+2			;High word of Send Structure Pointer
				adc		#^dib.trx_buff
				ldy		#dib.trx_ptr+2
				sta		[next_dib],y
											;
											; Were we called from the
											; main outside loop?
											;
				lda		|main_caller
				bne		@skip_links			; No.  Do device links.
											;
											; Zero out the HEAD LINK and FORWARD LINK
											; pointers.
											;
				ldy		#dib.headlnk
				sta		[next_dib],y		;Acc = 0. See Load.
				ldy		#dib.fdvclnk
				sta		[next_dib],y
				ldy		#dib.headptr
				sta		[next_dib],y		;Acc = 0. See Load.
				ldy		#dib.fdvcptr
				sta		[next_dib],y
				ldy		#dib.headptr+2
				sta		[next_dib],y		;Acc = 0. See Load.
				ldy		#dib.fdvcptr+2
				sta		[next_dib],y
											;
											; Set slot and device words in dib
											;
				lda		[dvc_list]

				ldy		#dib.slotnum
				sta		[next_dib],y

				ldy		#$0002
				lda		[dvc_list],y

				ldy		#dib.unitnum
				sta		[next_dib],y
											;
											; Set pointer to the descriptive or
											; device name so that we can rev the
											; name to prevent duplicates.
											;
@skip_links		clc
				lda		<next_dib
				adc		#dib.namelen
				sta		<scsi_zp0
				lda		<next_dib+2
				adc		#^dib.namelen
				sta		<scsi_zp0+2
											;
											; Was internal or externally called?
											; If internal, then we need to update
											; the major revision part of the
											; descriptive name.  If it was external,
											; then we will update the minor revision
											; part only.
											;
				lda		|main_caller
				beq		@do_major
											;
											; Do the minor revision.
											;
				jsr		minor_update
				bra		@clear_busy
											;
											; Do the major revision.
											;
@do_major		jsr		major_update
											;
											; Clear artifacts.  We copied this data
											; from the first dib that has the
											; call_active bit set.  Clear the sucker.
											;
@clear_busy		ldy		#dib.dvcchar
				lda		[next_dib],y
				and		#call_active--\
						$ffff
				sta		[next_dib],y
											;
											; Clear the ProDOS Bit.
											;
				ldy		#dib.dvcflag
				lda		[next_dib],y
				and		#pdos_part--\
						$ffff
				sta		[next_dib],y
											;
											; Inc the DIB Count at this point.
											; Every DIB that is succesfully built
											; will be modified by the above code.
											;
				stz		|main_caller
				inc		|tot_dib_cnt
				clc
				rts

				ENDP

				EJECT

;*******************************************************
;
;	MAJOR_UPDATE
;
;	This routine updates the major revision portion of
;	the descriptive name portion of the DIB.
;
;	Inputs:		<DIB_PTR		=	DIB Location
;				Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Outputs:	Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Errors:		Better be none.
;
;*******************************************************

				EXPORT	major_update
major_update	PROC
									;
									; Change name so as not to conflict
									; with any other name already built.
									; All names end with '00' - '99'.
									;
				short

				ldx		#$02		;Don't change this value.
				sec
				lda		[scsi_zp0]
									;
									; Reset Minor Revision to '00'.
									;
				pha
				tay
				lda		#'0'
				sta		[scsi_zp0],y
				dey
				sta		[scsi_zp0],y
				pla

				sbc		#$03
				tay
@name_loop		lda		[scsi_zp0],y
				inc		a
				cmp		#'9'+1
				blt		@update
				lda		#'0'
				dex
@update			sta		[scsi_zp0],y
				dey
				dex
				beq		@name_loop

				longmx

				rts

				ENDP

				EJECT

;*******************************************************
;
;	MINOR_UPDATE
;
;	This routine updates the minor revision portion of
;	the descriptive name portion of the DIB.
;
;	Inputs:		<DIB_PTR		=	DIB Location
;				Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Outputs:	Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Errors:		Better be none.
;
;*******************************************************

				EXPORT	minor_update
minor_update	PROC
									;
									; Change name so as not to conflict
									; with any other name already built.
									; All names end with '00' - '99'.
									;
				short

				ldx		#$02		;Don't change this value.
				lda		[scsi_zp0]
									;
									; Check the length of the string.  If
									; it is different from the one in the
									; Default DIB, then we need to assume
									; that it has been renamed.  In this
									; case, we will crank the count up by
									; 2 but not past the max count of 31
									; and append the version number
									; change to that.
									;
				cmp		|default_dib+\
						dib.namelen
				beq		@len_ok
									;
									; Has it already been adjusted?
									;
				tay
				lda		[scsi_zp0],y
				cmp		#'0'
				blt		@fix_it
				cmp		#'9'+1
				bge		@fix_it
				dey
				lda		[scsi_zp0],y
				cmp		#'0'
				blt		@fix_it
				cmp		#'9'+1
				bge		@fix_it
				iny
				bra		@name_loop

@fix_it			lda		[scsi_zp0]
				inc		a
				inc		a
				cmp		#$31
				blt		@set_new_len

				lda		#$31
@set_new_len	sta		[scsi_zp0]

@len_ok			tay
@name_loop		lda		[scsi_zp0],y
				inc		a

				cmp		#'1'
				blt		@force_zero

				cmp		#'9'+1
				blt		@update

@force_zero		lda		#'0'
				dex
@update			sta		[scsi_zp0],y
				dey
				dex
				beq		@name_loop

				longmx

				rts

				ENDP

				EJECT

;____DIB_Support_____
;*******************************************************
;
;	This call is used to determine if the current DIB is
;	the first device.  This routine will return true
;	(with the carry clear) if the device is unlinked or,
;	if linked, then it will return true if this is the
;	head device.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if Unit is not the first
;				device or it's equivelent.
;
;*******************************************************

				EXPORT	chk_first_dvc
chk_first_dvc	PROC

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

				IF			part_suprt = true		THEN

									;
									; Check Linkes to see if single device.
									;
				ldy		#dib.headlnk
				lda		[dib_ptr],y
				bne		@check_devnum
									;
									; Head Link = 0.  Return True.
									;
@true			clc
				rts
									;
									; Check to see if the Device Number and
									; Head Links are the same.  If not then
									; we return false (carry set).
									;
@check_devnum	ldy		#dib.devnum
				cmp		[dib_ptr],y
				beq		@true
									;
									; Return False.
									;
				sec
				rts

				ELSE

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

				lda		#null
				clc
				rts

				ENDIF

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


				ENDP

				EJECT

;*******************************************************
;
;	This call checks to see if the other device tied to
;	it are offline.  If they are then the carry will be
;	clear on exit.  If this device is tied to any other
;	device that is currently online, then the carry will
;	be set on exit.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if Unit is not the only
;				device in this link that is still
;				online.
;
;*******************************************************

				EXPORT	chk_lnk_offline
chk_lnk_offline	PROC

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

				IF			part_suprt = true		THEN

									;
									; Get the Head Pointer. Check for null.
									;
				clc
				ldy		#dib.headptr
				lda		[dib_ptr],y
				sta		<scsi_zp6
				beq		@over_0
				sec
@over_0			ldy		#dib.headptr+2
				lda		[dib_ptr],y
				sta		<scsi_zp6+2
									;
									; Check if ­ null.
									;
				bne		@check_it_loop
				bcs		@check_it_loop
									;
									; It was null.  This may be the head
									; of the link. Check it out.
									;
				ldy		#dib.fdvcptr
				lda		[dib_ptr],y
				ldy		#dib.fdvcptr+2
				ora		[dib_ptr],y
				beq		@no_link	;No. Return to the caller with the carry clear.
									;
									; Yes. This was the head of the link.
									; Set our pointer to this DIB.
									;
				lda		<dib_ptr
				sta		<scsi_zp6
				lda		<dib_ptr+2
				sta		<scsi_zp6+2
									;
									; At this point, 'scsi_zp6' points to the
									; head of the link.  Now walk the links
									; checking for online.  If a device is
									; online we will need to check if it is the
									; callers device. if so, then we will
									; continue the search.  If not, we will then
									; exit with the carry set.
									;
@check_it_loop	ldy		#dib.dvcflag

				lda		[scsi_zp6],y
				and		#dvc_online		;Is it online?
				beq		@not_online		;No.

				lda		[scsi_zp6],y	;Yes.  Maybe.  Check hard offline
				and		#dvc_hardofl	;Is it hard offline?
				bne		@not_online		;Yes.

				lda		<scsi_zp6+2	;Is it the caller device?
				cmp		<dib_ptr+2
				bne		@linked		;No.
				lda		<scsi_zp6
				cmp		<dib_ptr
				bne		@linked		;No.
									;
									; Point to next device.
									;
@not_online		clc
				ldy		#dib.fdvcptr
				lda		[scsi_zp6],y
				tax
				beq		@over_2
				sec
@over_2			ldy		#dib.fdvcptr+2
				lda		[scsi_zp6],y
				bne		@save_it
				bcc		@no_link
@save_it		sta		<scsi_zp6+2
				stx		<scsi_zp6

				bra		@check_it_loop
				
									;
									; Only device online.
									;
@no_link		clc
				rts
									;
									; Not the only device online.
									;
@linked			sec
				rts

				ELSE

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

				lda		#null
				clc
				rts

				ENDIF

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


				ENDP

				EJECT

;*******************************************************
;
;	'wake_me_up'
;
;	This call walks the DIBs, slaps them in the face and
;	pours cold water on them.  It seems that they have
;	been sleeping while P8 was running.
;
;				!!!! WAAAAAAAAKE  UUUUUUUP !!!!
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		None.
;
;*******************************************************

				EXPORT	wake_me_up
wake_me_up		PROC

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

				IF		warm_ss_suprt = true		THEN

											;
											; Start with the first DIB after
											; the default DIB
											;
				lda		|default_dib
				sta		<first_dib
				sta		<next_dib
				lda		|default_dib+2
				sta		<first_dib+2
				sta		<next_dib+2
											;
											; Is he asleep?
											;
@check_it_loop	ldy		#dib.dvcflag
				lda		[next_dib],y
				tax
				and		#relaxing
				beq		@next_dib			;No he is not asleep
											;
											; Yes he is!  Is he also Hard
											; Offline?
											;
				txa
				and		#dvc_hardofl
				beq		@not_hardofl		;No.  Wake him up all the way then.

				txa							;Yes.  Only Clear Sleep Bit.
				and		#relaxing--\		;Don't set online.
						$ffff
				sta		[next_dib],y

				bra		@next_dib
											;
											; Yes he is, wake him up.
											;
@not_hardofl	txa
				and		#relaxing--\
						$ffff
				ora		#dvc_online
				sta		[next_dib],y
											;
											; Advance to the next one
											;
@next_dib		clc
				ldy		#dib.linkptr
				lda		[next_dib],y
				tax
				beq		@over_0
				sec
@over_0			ldy		#dib.linkptr+2
				lda		[next_dib],y
				sta		<next_dib+2
				stx		<next_dib
											;
											; Check if ­ null.
											;
				bne		@check_it_loop
				bcs		@check_it_loop
											;
											; That was the last one.
											;
											; By default of the branches above,
											; the Acc. = 0 and the carry is clear.
											;
				rts

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

 				ELSE

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

				clc
				rts

 				ENDIF

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


				ENDP

				EJECT

;*******************************************************
;
;	'fix_unit_1'
;
;	This call walks the DIBs, ensuring that unit 1 is
;	the first ProDOS Partition on the disk..
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		None.
;
;*******************************************************

				EXPORT	fix_unit_1
fix_unit_1		PROC

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

				IF		part_suprt = true		THEN

										;
										; Check the pdos Bit for the Unit number 1.
										; This may or may not be the head device.
										;
				ldy		#dib.headptr
				lda		[dib_ptr],y
				sta		<scsi_zp0
				ldy		#dib.headptr+2
				lda		[dib_ptr],y
				sta		<scsi_zp0+2
				ora		<scsi_zp0		;Are we at the Head Now?
				bne		@check_it		;No.
										;
										; We were already at the head.  Set pointer
										; to this DIB before going on.
										;
				lda		<dib_ptr
				sta		<scsi_zp0
				lda		<dib_ptr+2
				sta		<scsi_zp0+2
										;
										; Is this unit number 1?
										;
@check_it		ldy		#dib.unitnum
				lda		[scsi_zp0],y
				and		#max_p_mask
				beq		@unit_1			;Yes.
										;
										; No.  It's not unit number one.  Check the
										; next DIB in the link if it is non-zero.
										;
				clc
				ldy		#dib.fdvcptr
				lda		[scsi_zp0],y
				tax

				beq		@over_sec
				sec

@over_sec		ldy		#dib.fdvcptr+2
				lda		[scsi_zp0],y

				bne		@set_ptr
				bcc		@out

@set_ptr		sta		<scsi_zp0+2
				stx		<scsi_zp0
				bra		@check_it
										;
										; This is unit 1.  Is it a ProDOS Partition?
										;
@unit_1			ldy		#dib.dvcflag
				lda		[scsi_zp0],y
				and		#pdos_part
				bne		@out			;It's ProDOS, Get out of here.
										;
										; We're out of here.
										;
@out			clc
				rts

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

 				ELSE					;part_suprt = true

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

				clc
				rts

 				ENDIF					;part_suprt = true

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


				ENDP					;fix_unit_1

				EJECT

;*******************************************************
;
;	ADD_DIB_PTR
;
;	This routine adds a pointer to a list of dibs to be
;	installed by the 'POST_DRIVER_INSTALL' System
;	Service call.  Refer to the System Service ERS for
;	details of how this works.
;
;	Inputs:		Acc				=	Unspecified
;				Y register		=	High word of dib ptr
;				X register		=	Low word of dib ptr
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Outputs:	<DIB_PTR		=	Empty DIB if found
;				Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Errors:		None
;
;*******************************************************

				EXPORT	add_dib_ptr
add_dib_ptr		PROC

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

				IF			part_suprt = true		THEN

										;
										; Check to see if this has an overide
										; placed on it.
										;
				lda		|dpi_overide
				bmi		@exit			;Yes.
										;
										; Is this the first time that this has been
										; called for this session.  Only the
										; do_post_install can clear this flag.
										;
				lda		|new_list
				beq		@start_over		; Acc. = 0
										;
										; Inc the count of new devices to be started.
										;
				lda		|new_dib_cnt
@start_over		pha
				inc		a
				sta		|new_dib_cnt
				sta		|new_list
				pla
												;
												; Calculate offset for next ptr.
												;
				asl		a
				asl		a
				pha
				txa
				plx
				sta		|new_dib_list,x			;Place Ptr in list
				sta		<scsi_zp0				;and on zero page.

				tya
				sta		|new_dib_list+2,x		;Place Ptr in list
				sta		<scsi_zp0+2				;and on zero page.

				ldy		#dib.dvcflag
				lda		[scsi_zp0],y
				ora		#cold_dib				;New DIB.  Do Cold Start.
				sta		[scsi_zp0],y

@exit			clc
				rts

				ELSE

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

				lda		#null
				clc
				rts

				ENDIF

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

				ENDP

				EJECT

;*******************************************************
;
;	DO_POST_INSTALL
;
;	This routine calls the 'POST_DRIVER_INSTALL' System
;	Service call.  Refer to the System Service ERS for
;	details of how this works.
;
;	Inputs:		Acc				=	Unspecified
;				Y register		=	High word of dib ptr
;				X register		=	Low word of dib ptr
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Outputs:	<DIB_PTR		=	Empty DIB if found
;				Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Errors:		None
;
;*******************************************************

				EXPORT	do_post_install
do_post_install	PROC

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

				IF			part_suprt = true		THEN

										;
										; Check to see if this has an overide
										; placed on it.
										;
				lda		|dpi_overide
				bmi		@exit			;Yes.
										;
										; Clear flag indicating that the call was
										; sent.
										;
				stz		|pdi_flag
				stz		|new_list
										;
										; Check the count of new devices to be
										; started.
										;
				lda		|new_dib_cnt
				beq		@exit
										;
										; Make the call
										;
				lda		|gsos_dpage
				tcd
				ldx		#new_dib_cnt
				ldy		#^new_dib_cnt

				jsl		install_driver
				pha
				lda		|direct_page
				tcd
				pla

				bcs		@exit
				dec		|pdi_flag
				
@exit			clc
				rts

				ELSE

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

				lda		#null
				clc
				rts

				ENDIF

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

				ENDP

				EJECT

;____Part_Support____

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

				IF			part_suprt = true		THEN

;*******************************************************
;
;	READ_PM_BLK
;
;	
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if operation could not be
;				completed.  Out of memory for example.
;
;*******************************************************

				EXPORT	read_pm_blk
read_pm_blk		PROC
										;
										; Tell the Main Driver where
										; our command structure resides.
										;
				lda		#@read_part
				sta		<scsi_mdrvr
				lda		#^@read_part
				sta		<scsi_mdrvr+2
										;
										; And our buffer
										;
				lda		#internal_buff
				sta		<buff_ptr
				lda		#^internal_buff
				sta		<buff_ptr+2
										;
										; Was this from the STARTUP Call?
										;
				bit		|rebuild
				bmi		@over			;No.
										;
										; Yes, then stuff the pointer to
										; the dib that we are building into
										; the Direct Page locations.  The
										; main driver will use this to build
										; the SCSI READ BLOCK Command that
										; we will issue.
										;
				lda		<next_dib
				sta		<dib_ptr
				lda		<next_dib+2
				sta		<dib_ptr+2
										;
										; And our length from the DIB's
										; Block Size value
										;
@over			lda		#block_size
				sta		<rqst_cnt
				lda		#^block_size
				sta		<rqst_cnt+2
										;
										; Was this from a Status or Control Call?
										;
				bit		|stat_cont
				bmi		@block_set
										;
										; Set block to read for the next
										; partition map block.  This field
										; is initialized to zero.  Because
										; the first partition map block is
										; on block 1, this will be initialized
										; to zero and then blindly incremented
										; until we are finished.
										;
										; *** This is High --> Low format ***
										;
				lda		|rpm_blk_num
				xba
				inc		a
				xba
				sta		|rpm_blk_num
										;
										; Set internal command flag
										;
@block_set		stz		|stat_cont		;We used the flag, now clear it.
				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

@rts			rts
										;
										; Data for the READ BLOCK Command
										;
@read_part		dc.b		$08
				dc.b		null
				EXPORT		rpm_blk_num
rpm_blk_num		dc.w		null
				dcb.b		10,null

				ENDP

				EJECT

;*******************************************************
;
;	'write_pm_blk'
;
;	
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if operation could not be
;				completed.  Out of memory for example.
;
;*******************************************************

				EXPORT	write_pm_blk
write_pm_blk	PROC
										;
										; Tell the Main Driver where
										; our command structure resides.
										;
				lda		#@write_part
				sta		<scsi_mdrvr
				lda		#^@write_part
				sta		<scsi_mdrvr+2
											;
											; And our buffer
											;
				lda		#internal_buff
				sta		<buff_ptr
				lda		#^internal_buff
				sta		<buff_ptr+2
										;
										; And our length from the DIB's
										; 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_cont
				sta		|call_type
										;
										; Issue the call.
										;
				jsr		check_532_rw

@rts			rts
										;
										; Data for the READ BLOCK Command
										;
@write_part		dc.b		$0A
				dc.b		null
				EXPORT		wpm_blk_num
wpm_blk_num		dc.w		null
				dcb.b		10,null

				ENDP

				EJECT

;*******************************************************
;
;	CHK_MAP
;
;	This routine checks the validity of the partition
;	map block currently loaded in the internal buffer.
;	This need only be called once per device.  If the
;	map is valid, then the carry will be clear.  The 'Z'
;	flag will also be set if this is not the first time
;	that this routine was called for this device.  If
;	the carry is set, then the map was invalid and the
;	block count at 'T_DVC_BLOCKS' has been placed in the
;	DIB pointed to by 'DIB_PTR'.  The starting block will
;	also have been set to null.
;
;	Inputs:		<DIB_PTR		=	DIB Location
;				|T_DVC_BLOCKS	=	Total blocks for this device
;				Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Outputs:	Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;								=	Z = 0 if routine called before
;								=	C = 1 if not a partition map
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Errors:		Carry set if 'internal_buff does not
;				contain a valid Partition map entry.
;
;*******************************************************

				EXPORT	chk_map
chk_map			PROC
										;
										; Valid Partition Map?
										;
@over_0			inc		|first_time		;Don't need to do this but once.
				bne		@clc			;Already done if taken.
				
				lda		|pm.Sig\
						+internal_buff
				cmp		#Part_sig
				bne		no_partition	;No.

@clc			clc
				rts
										;
										; We didn't have a valid partition
										; map, so this device will be
										; treated as a single non-partitioned
										; device.
										;
				EXPORT	no_partition
										;
										; Issue the READ CAPACITY Command.
										;
no_partition	jsr		read_capacity
				bcs		@over_0			;Was there an error?
										;
										; Get the Block Count (Stored
										; High >> Low.  Must be switched
										; to Low >> High).  This is the last
										; readable block number.  Add 1 to
										; it for comparison reasons.
										;
				lda		|block.count\
						+internal_buff\
						+2
				xba
				adc		#$0001

				ldy		#dib.blkcnt
				sta		[dib_ptr],y

				lda		|block.count\
						+internal_buff
				xba
				adc		#null

				ldy		#dib.blkcnt+2
				sta		[dib_ptr],y

@over_0			ldy		#dib.start_blk
				lda		#null
				sta		[dib_ptr],y
				ldy		#dib.start_blk+2
				sta		[dib_ptr],y
										;
										; Set Read and Write Enables
										;
				lda		>default_dib+dib.dvcchar	;Get Default Settings for
				and		#write_allow++\				;Write Enable
						read_allow++\				;Read Enable
						format_allow				;And Format Allowed
				ldy		#dib.dvcchar
				ora		[dib_ptr],y					;Combine with the current
				sta		[dib_ptr],y					;Settings for this device
										;
										; Mark DIB as online and switched.
										;
				ldy		#dib.dvcflag
				lda		[dib_ptr],y
				and		#dvc_hardofl--\
						$ffff
				ora		#dvc_switch++\
						dvc_online
				sta		[dib_ptr],y

				sec
				rts

				ENDP

				EJECT

;*******************************************************
;
;	'check_map_entry'
;
;	Check the Partition Map Entry currently loaded in
;	the internal buffer.  If it is of type
;	'Apple_partition_map', 'Apple_Driver', 'Apple_Free',
;	or 'Apple_Scratch', then this routine will return
;	with the carry set.  Any other type will return
;	with the carry clear.  In addition, 'Apple_ProDOS'
;	will return with the overflow flag set.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if Partition Map Entry is not
;				for a valid Volume.  Overflow set if the
;				Partition is a ProDOS Partition.
;
;*******************************************************

				EXPORT	check_map_entry
check_map_entry	PROC
										;
										; This routine runs in
										; 8 bit m and x only.
										;
				short
										;
										; Is it ProDOS?
										;
				ldx		#@p_map-@prodos-2
				ldy		#null

@loop			lda		|pm.PartType\
						+internal_buff,y
				beq		@over_0			;Yes or null.  Either way we do the rest.
				cmp		@prodos,y
				beq		@next
				eor		#'A'--'a'
				cmp		@prodos,y
				bne		@over_0			;No.  Continue on.
@next			iny
				dex
				bpl		@loop
				brl		@pdos_part		;Skip over 8 bit code area to 16 bit area.
										;
										; Is it the Partition Map?
										;
@over_0			ldx		#32-1
				ldy		#null

@loop_0			lda		|pm.PartType\
						+internal_buff,y
				beq		@skip			;Yes or null.  Either way we skip it.
				cmp		@p_map,y
				beq		@next_0
				eor		#'A'--'a'
				cmp		@p_map,y
				bne		@over_1			;No.  Continue on.
@next_0			iny
				dex
				bpl		@loop_0
				bra		@skip			;Yes. Skip it.
										;
										; Is it a FREE Partition?
										;
@over_1			ldx		#32-1
				ldy		#null

@loop_1			lda		|pm.PartType\
						+internal_buff,y
				beq		@skip			;Yes or null.  Either way we skip it.
				cmp		@free,y
				beq		@next_1
				eor		#'A'--'a'
				cmp		@free,y
				bne		@over_2			;No.  Continue on.
@next_1			iny
				dex
				bpl		@loop_1
				bra		@skip			;Yes. Skip it.
										;
										; Is it a SCRATCH Partition?
										;
@over_2			ldx		#32-1
				ldy		#null

@loop_2			lda		|pm.PartType\
						+internal_buff,y
				beq		@skip			;Yes or null.  Either way we skip it.
				cmp		@scratch,y
				beq		@next_2
				eor		#'A'--'a'
				cmp		@scratch,y
				bne		@over_3			;No.  Continue on.
@next_2			iny
				dex
				bpl		@loop_2
										;
										; Is it a Driver Partition?
										;
@over_3			ldx		#32-1
				ldy		#null

@loop_3			lda		|pm.PartType\
						+internal_buff,y
				beq		@skip			;Yes or null.  Either way we skip it.
				cmp		@driver,y
				beq		@next_3
				eor		#'A'--'a'
				cmp		@driver,y
				bne		@over_4			;No.  Continue on.
@next_3			iny
				dex
				bpl		@loop_3
										;
										; At this point the Current Partition
										; Type matched one of the three types
										; checked for.  Exit with the carry
										; set.
										;
@skip			longmx
				sec
				rts
										;
										; At this point the Current Partition
										; Type did not match one of the three
										; types checked for and it was not a
										; ProDOS Partition..  Exit with the
										; Carry and Overflow bits clear.
										;
@over_4			longmx
				clc
				clv
				rts
										;
										; At this point the Current Partition
										; Type is a ProDOS Partition.  Exit with
										; the Carry clear and Overflow set.
										;
@pdos_part		longmx
				clc
				sep		#%01000000		;Set the v flag
				rts
										;
										; Inactive Partion Types to check for.
										;
				STRING		ASIS

@prodos			dc.b		'Apple_ProDOS'
				dc.b		$00
@driver			dc.b		'Apple_Driver'
				dc.b		$00
@p_map			dc.b		'Apple_partition_map'	
				dc.b		$00
@free			dc.b		'Apple_Free'
				dc.b		$00
@scratch		dc.b		'Apple_Scratch'
				dc.b		$00

				STRING		PASCAL

				ENDP

				EJECT

				ELSE					;part_suprt = true

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

				EXPORT		read_pm_blk
read_pm_blk		PROC

				EXPORT		check_map_entry
check_map_entry
				lda		#null
				clc
				clv
				rts


				EXPORT		rpm_blk_num
rpm_blk_num		dc.w	null

				EXPORT		chk_map
chk_map
										;
										; We can't have a valid partition
										; map, so this device will be
										; treated as a single non-partitioned
										; device.
										;
				EXPORT	no_partition
										;
										; Issue the READ CAPACITY Command.
										;
no_partition	jsr		read_capacity
				bcs		@over_0			;Was there an error?
										;
										; Get the Block Count (Stored
										; High >> Low.  Must be switched
										; to Low >> High).  This is the last
										; readable block number.  Add 1 to
										; it for comparison reasons.
										;
				lda		|block.count\
						+internal_buff\
						+2
				xba
				adc		#$0001

				ldy		#dib.blkcnt
				sta		[dib_ptr],y

				lda		|block.count\
						+internal_buff
				xba
				adc		#null

				ldy		#dib.blkcnt+2
				sta		[dib_ptr],y

@over_0			ldy		#dib.start_blk
				lda		#null
				sta		[dib_ptr],y
				ldy		#dib.start_blk+2
				sta		[dib_ptr],y
										;
										; Mark DIB as online and switched.
										;
				ldy		#dib.dvcflag
				lda		[dib_ptr],y
				and		#dvc_hardofl--\
						$ffff
				ora		#dvc_switch++\
						dvc_online
				sta		[dib_ptr],y

				sec
				rts

				ENDIF					;part_suprt = true

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

;____Cache_Suport____

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

				IF	cache_blks = true	THEN

;*******************************************************
;
;	'r_all_in_cache'
;
;	This routine is used to check if the entire
;	requested block count is in the cache.  This routine
;	will check each block individually, incrementing the
;	block count and subtracting the block size from the
;	request count until either a block is found that is
;	not in the cache (Exit Carry Set) or request count
;	has been exausted (Exit Carry Clear).
;
;	On entry this routine clears some values to indicate
;	it's starting point for this call.  As it goes
;	throughthe cache looking for blocks, it will fetch
;	the block from the cache and advance the starting
;	point.  What this does is eliminate double searches
;	throughthe cache and if a read is needed, these
;	blocks will not have to be transfered.  When this
;	routine exits, the caller will think that the first
;	block that we found that was not in the cache was
;	the first block requested.  For this reason, any
;	code that uses this call will need to preserve those
;	values so that they can be restored on exit.
;
;	If we receive a Read Call for 256 blocks and the
;	first 128 are in the cache, then we don't want to
;	re-read them, and we don't want to do a find again
;	later when we fetch them.  let's fetch them now and
;	advance our pointers.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				<buff_ptr	=	Original Starting Buffer
;				<rqst_cnt	=	Original Request Count
;				<block_num	=	Original Starting Block
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				<buff_ptr	=	New Starting Buffer
;				<rqst_cnt	=	New Request Count
;				<block_num	=	New Starting Block
;				Data Bank	=	Ours
;
;	Errors:		Acc = 0 and Carry set if 1 or more
;				blocks of the request are not in the
;				cache.
;
;				Acc <> 0 if any other error occurs.
;				Error code is in Acc.
;
;*******************************************************

				EXPORT	r_all_in_cache
r_all_in_cache	PROC
										;
										; Switch Direct Pages.
										;
				lda		|gsos_dpage
				tcd
										;
										; Get the requested count and devide
										; by block size to get the loop counter.
										; If Count = 0, then exit.
										;
				lda		<rqst_cnt
				sta		|divend
				lda		<rqst_cnt+2
				sta		|divend+2

				ora		<rqst_cnt
				bne		@continue

				lda		|direct_page
				tcd
				lda		#null
				clc
@rts			rts

										;
										; Call divide routine.
										;
@continue		lda		<blk_size
				sta		|divsor

				jsr		divide
				bcc		@over_divide

				lda		|direct_page
				tcd

				lda		#drvr_bad_cnt	;non-integral rqst count.
				sec
				rts
										;
										; Count = count -1.  This way we can
										; use a BMI to exit with.
										;
@over_divide	lda		|result+2
				sta		@blk_cnt+2
				lda		|result
				sta		@blk_cnt
				bne		@over_dec_high
				dec		@blk_cnt+2
@over_dec_high	dec		@blk_cnt
										;
										; Setup is now completed.  Now we start to
										; check to see if the blocks are in the
										; cache.
										;
@chk_blk_loop	sec						;return error if not in cache. Don't add.
				jsr		|get_from_cache
				bcc		@found
				lda		#null			;Not there.  Clear the Acc. and Exit.
				bra		@restore
										;
										; Are there any more to do?
										;
@found			lda		@blk_cnt
				bne		@over_dec
				dec		@blk_cnt+2
				bpl		@over_dec
										;
										; No. Count is exausted.
										;
				lda		#null
				clc
				bra		@restore
										;
										; Yes. Finish the DEC and update the
										; Buffer Pointer, Request Count, and
										; the Block Number.
										;
@over_dec		dec		@blk_cnt

				clc						;Bump Buffer Pointer
				lda		<buff_ptr
				adc		<blk_size
				sta		<buff_ptr
				lda		<buff_ptr+2
				adc		#null
				sta		<buff_ptr+2

				sec						;Back off Request Count
				lda		<rqst_cnt
				sbc		<blk_size
				sta		<rqst_cnt
				lda		<rqst_cnt+2
				sbc		#null
				sta		<rqst_cnt+2

				inc		<block_num		;Inc the Block Number
				bne		@chk_blk_loop
				inc		<block_num+2
				bra		@chk_blk_loop
										;
										; We are finished.  We don't need to
										; know if we were succesfull.  We only
										; need to restore the state of the
										; corrupted direct page values while
										; preserving the Acc. and Carry bit.
										;
@restore		pha
				php

				ldx		|direct_page

				lda		<buff_ptr
				sta		>buff_ptr,x
				sta		|c_strt_buffer
				lda		<buff_ptr+2
				sta		>buff_ptr+2,x
				sta		|c_strt_buffer+2

				lda		<rqst_cnt
				sta		>rqst_cnt,x
				sta		|c_strt_rqst_cnt
				lda		<rqst_cnt+2
				sta		>rqst_cnt+2,x
				sta		|c_strt_rqst_cnt+2

				lda		<block_num
				sta		>block_num,x
				sta		|c_strt_blk_num
				lda		<block_num+2
				sta		>block_num+2,x
				sta		|c_strt_blk_num+2
										;
										; Validate Flag for the Read Cache
										; Routines
										;
				dec		|cache_valid

				txa
				tcd

				plp
				pla
				rts
										;
										; Internal Data
										;
@blk_cnt		dc.l	null
										;
										; The following values are used to advance
										; the starting point of the cache calls.
										;
				EXPORT		c_strt_buffer
				EXPORT		c_strt_rqst_cnt
				EXPORT		c_strt_blk_num
				EXPORT		cache_valid

c_strt_buffer	dc.l	null			;Starting Buffer Pointer
c_strt_rqst_cnt	dc.l	null			;Starting Request Count
c_strt_blk_num	dc.l	null			;Starting Block Number
cache_valid		dc.w	null

				ENDP

				EJECT

;*******************************************************
;
;	'w_update_cache'
;
;	This routine is used when writting data to the disk
;	with a cache priority of $0000.  If the block is in
;	the cache, then the cache will be updated.  If not,
;	then nothing will happen.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;				Carry Clear	=	Always place in cache
;				Carry set	=	Only place in cache if
;								already there.
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if requested block is not
;				added to the cache.
;
;*******************************************************

				EXPORT	w_update_cache
w_update_cache	PROC
										;
										; Zero Deferred Write failed flag.
										;
				stz		@deferred
										;
										; Preserve Carry Flag for later.
										;
				php
										;
										; Set GS/OS Direct Page
										;
				lda		|gsos_dpage
				tcd
										;
										; Preserve Block Number and request
										; count.  Set count to 1 block per
										; call.
										;
				lda		<buff_ptr
				sta		@orig_buff

				lda		<rqst_cnt
				sta		@bytes_rem

				lda		<block_num
				sta		@start_blk

				lda		<buff_ptr+2
				sta		@orig_buff+2

				lda		<rqst_cnt+2
				sta		@bytes_rem+2

				lda		<block_num+2
				sta		@start_blk+2
										;
										; Is the requested block in the cache?
										;
@do_cache		plp
				php
				jsr		put_in_cache
				bcc		@cached			;Carry set only if a deferred write
				dec		@deferred		;failed to go in the cache.
										;
										; Advance to the next block.
										;
@cached			inc		<block_num
				bne		@over
				inc		<block_num+2
										;
										; Bump Buffer Pointer.
										;
@over			clc
				lda		<buff_ptr
				adc		<blk_size
				sta		<buff_ptr
				bcc		@over_1
				inc		<buff_ptr+2
										;
										; Any Data Left
										;
@over_1			sec
				lda		@bytes_rem
				sbc		<blk_size
				sta		@bytes_rem
				sta		|temp_acc

				lda		@bytes_rem+2
				sbc		#null
				sta		@bytes_rem+2
				ora		|temp_acc

				bne		@do_cache		;Yes.
				clc
										;
										; Restore Values.
										;

@restore		lda		@orig_buff
				sta		<buff_ptr

				lda		@start_blk
				sta		<block_num

				lda		@orig_buff+2
				sta		<buff_ptr+2

				lda		@start_blk+2
				sta		<block_num+2
										;
										; Exit.
										;
				lda		|direct_page
				tcd

				lda		@deferred
				beq		@clean_out
				plp						;Restore the Stack.
				sec
				rts

@clean_out		plp						;Restore the Stack.
				clc
				rts
										;
										; Internal Data
										;
@deferred		dc.w	null
@orig_buff		dc.l	null
@bytes_rem		dc.l	null
@start_blk		dc.l	null

				ENDP

				EJECT

;*******************************************************
;
;	'put_in_cache'
;
;	This routine is used to place the current block in
;	the cache.  First we will check if the requested
;	block is in the cache.  If the block is not in the
;	cache then we will add it.  if there is no room and
;	the driver call was issued in deferred mode then we
;	will exit with the carry set.  In all other cases
;	we will exit Carry Clear.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;				Carry Clear	=	Always place in cache
;				Carry set	=	Only place in cache if
;								already there.
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if requested block is not
;				added to the cache in deferred mode
;				only.
;
;*******************************************************

				EXPORT	put_in_cache
put_in_cache	PROC
										;
										; Is the requested block in the cache?
										;
				php

				clc
				jsl		cache_find_blk

				bcc		@move_it_in		;Yes. It is.
				plp
				bcs		@clc
										;
										; Not there.  Allocate space.
										;
				jsl		cache_add_blk
				bcc		@skip_fix
				lda		<cache_prio		;Deferred Mode?
				bpl		@clc			;No.  Exit Carry Clear.
				rts						;Yes. Exit carry set
										;
										; Move the block in.
										;
@move_it_in		plp						;Restore the stack.

@skip_fix		pei		<buff_ptr+2
				pei		<buff_ptr
				pei		<cache_ptr+2
				pei		<cache_ptr
				pea		$0000
				pei		<blk_size
				pea		|move_sinc_dinc
				jsl		move_info
										;
										; Exit.
										;
@clc			clc
@rts			rts

				ENDP

				EJECT

;*******************************************************
;
;	'r_get_cache'
;
;	This routine is used when reading data from the disk
;	with a cache priority of $0000.  If the block is in
;	the cache, then the data will be updated.  If not,
;	then nothing will happen.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;				Carry Clear	=	Always place in cache
;				Carry set	=	Only place in cache if
;								already there.
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if requested block is not
;				added to the cache.
;
;*******************************************************

				EXPORT	r_get_cache
r_get_cache		PROC
										;
										; Preserve Carry Flag for later.
										;
				php
										;
										; Check Validity of the call.
										;
				lda		|cache_valid
				bmi		@valid

				plp
				lda		#drvr_bad_code
				sec
				rts
										;
										; Set GS/OS Direct Page
										;
@valid			lda		|gsos_dpage
				tcd
										;
										; Get Request count.
										;
				lda		|c_strt_rqst_cnt
				sta		@bytes_rem
				lda		|c_strt_rqst_cnt+2
				sta		@bytes_rem+2
										;
										; Is the requested block in the cache?
										;
@do_cache		plp
				php
				jsr		get_from_cache
										;
										; Advance to the next block.
										;
				inc		<block_num
				bne		@over
				inc		<block_num+2
										;
										; Bump Buffer Pointer.
										;
@over			clc
				lda		<buff_ptr
				adc		<blk_size
				sta		<buff_ptr
				bcc		@over_1
				inc		<buff_ptr+2
										;
										; Any Data Left
										;
@over_1			sec
				lda		@bytes_rem
				sbc		<blk_size
				sta		@bytes_rem
				sta		|temp_acc

				lda		@bytes_rem+2
				sbc		#null
				sta		@bytes_rem+2
				ora		|temp_acc

				bne		@do_cache		;Yes.
										;
										; Exit.
										;
				stz		|cache_valid	;Cache Values no longer valid

				lda		|direct_page
				tcd

				plp						;Restore the Stack.
				clc
				rts
									;
									; Internal Data
									;
@bytes_rem		dc.l	null

				ENDP

				EJECT

;*******************************************************
;
;	'get_from_cache'
;
;	This routine is used to get the current block from
;	the cache.  First we will check if the requested
;	block is in the cache.  If the block is not in the
;	cache then we will exit with the Carry Set.  If it
;	is found, then we will move it to the requested ram
;	and exit Carry Clear.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if requested block is not
;				in the cache.
;
;*******************************************************

				EXPORT	get_from_cache
get_from_cache	PROC
										;
										; Is the requested block in the cache?
										;
				php

				clc
				jsl		cache_find_blk

				bcc		@move_it_in		;Yes. It is.
				plp
				bcs		@rts
										;
										; Not there.  Allocate space.
										;
				jsl		cache_add_blk
				bcs		@rts

				pei		<buff_ptr+2
				pei		<buff_ptr
				pei		<cache_ptr+2
				pei		<cache_ptr
				pea		$0000
				pei		<blk_size
				pea		|move_sinc_dinc
				jsl		move_info

				bra		@clc
										;
										; Move the block in.
										;
@move_it_in		plp						;Restore the stack.

@skip_fix		pei		<cache_ptr+2
				pei		<cache_ptr
				pei		<buff_ptr+2
				pei		<buff_ptr
				pea		$0000
				pei		<blk_size
				pea		|move_sinc_dinc
				jsl		move_info
										;
										; Exit.
										;
@clc			clc
@rts			rts

				ENDP

				EJECT

;*******************************************************
;
;	'ko_cache'
;
;	This routine is used to kill all blocks that are
;	cached that are included in a device specific write
;	type call.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		None.
;
;*******************************************************

				EXPORT	ko_cache
ko_cache		PROC
									;
									; Switch Direct Pages.
									;
				lda		|gsos_dpage
				tcd
									;
									; Preserve the block number on direct
									; page.
									;
				lda		<block_num
				sta		|scratch0
				lda		<block_num+2
				sta		|scratch1
									;
									; Set our starting block number on
									; direct page.
									;
				lda		|killer_blk
				sta		<block_num
				lda		|killer_blk+2
				sta		<block_num+2
									;
									; Get the requested count and devide
									; by block size to get the loop counter.
									; If Count = 0, then exit.
									;
				lda		<rqst_cnt+2
				sta		|divend+2
				lda		<rqst_cnt
				sta		|divend
				ora		<rqst_cnt+2
				bne		@do_devide

				clc
@rts			bra		@restore
									;
									; Call divide routine.
									;
@do_devide		lda		<blk_size
				sta		|divsor

				jsr		divide
				bcc		@over_divide
				lda		#drvr_bad_cnt	;non-integral rqst count.
				sec
				bra		@restore
									;
									; Count = count -1.  This way we can
									; use a BMI to exit with.
									;
@over_divide	lda		|result+2
				sta		@blk_cnt+2
				lda		|result
				sta		@blk_cnt
				bne		@over_dec_high
				dec		@blk_cnt+2
@over_dec_high	dec		@blk_cnt
									;
									; Setup is now completed.  Now we start to
									; check to see if the blocks are in the
									; cache.
									;
@chk_blk_loop	jsl		cache_kil_blk
									;
									; Are there any more to do?
									;
				lda		@blk_cnt
				bne		@over_dec
				dec		@blk_cnt+2
				bpl		@over_dec
									;
									; No. Count is exausted.
									;
				clc
				bra		@restore
									;
									; Yes. Finish the DEC and update
									; the block number.
									;
@over_dec		dec		@blk_cnt

				inc		<block_num
				bne		@chk_blk_loop
				inc		<block_num+2
				bra		@chk_blk_loop
									;
									; We are finished.  We don't need to
									; know if we were succesfull.  We only
									; need to restore the state of the
									; corrupted direct page values while
									; preserving the Acc. and Carry bit.
									;
@restore		pha
				php
				lda		|scratch0
				sta		<block_num
				lda		|scratch1
				sta		<block_num+2

				lda		|direct_page
				tcd

				plp
				pla
				rts
									;
									; Internal Data
									;
@blk_cnt		dc.l	null

				ENDP

				EJECT

				ELSE

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


put_in_cache	PROC		Export

				EXPORT		get_from_cache
				EXPORT		c_strt_buffer
				EXPORT		c_strt_rqst_cnt
				EXPORT		c_strt_blk_num
				EXPORT		cache_valid

get_from_cache	sec
				rts

c_strt_buffer
c_strt_rqst_cnt
c_strt_blk_num
cache_valid		dc.w	null

				ENDP

				EJECT

				ENDIF

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

;____Device_Tests____
;*******************************************************
;
;	'unit_state'
;
;	Tests the Target Device to see if it is ready to
;	accept our commands.  If the device responds with a
;	CHECK CONDITION, then we will find out why and
;	perform the correct actions.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;				<dib_ptr	=	Called DIB
;
;	Outputs:	Acc			=	Error Code if any
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;				<dib_ptr	=	Called DIB
;
;	Errors:		Carry set if Unit no ready.
;
;*******************************************************

				EXPORT	unit_state
unit_state		PROC

				stz		|un_formatted	;**** test Code ****
										;
										; 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).
										;
@try_again		jsr		test_unit_rdy	;Is there anything that we should know about?
				bcs		@turdy_fail		;Yes.
@rtg			brl		@ready_to_go	;No.

@turdy_fail

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

				IF			scsi_dtype = direct_acc		THEN

				cmp		#$FE16
				bne		@not_down
				brl		@off_line
@not_down		

				ENDIF

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

				lda		|auto_sense_data+\
						rqst_sens.sense_key
				beq		@rtg
				cmp		#$ffff
				beq		@try_again

				and		#$00ff
				cmp		#$0006
				beq		@do_switch

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

				IF			scsi_dtype = direct_acc		THEN

				cmp		#$0002
				bne		@on_line_hd
				brl		@off_line
@on_line_hd		lda		auto_sense_data+\		;Was it a RESET?
						rqst_sens.addnl_sens_code
				and		#$00fe
				cmp		#$0028			;Checking for $28 (Medium Changed), $29 (RESET)
				bne		@io_error

				ENDIF

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

				IF			scsi_dtype = apple_cd\
				OR			scsi_dtype = changer		THEN

				lda		auto_sense_data+\		;Was it a RESET?
						rqst_sens.addnl_sens_code
				and		#$00ff
				cmp		#$00b7			;Checking for $B7 (Media being mounted)
				beq		@do_switch
				cmp		#$00b0			;Checking for $B0 (NO MEDIA)
				bne		@on_line_cd
				brl		@off_line
@on_line_cd		and		#$00fe
				cmp		#$0028			;Checking for $28 (Medium Changed), $29 (RESET)

				beq		@no_error_cd
				brl		@io_error
@no_error_cd
										;
										; Set this location to a $B0.  This will
										; prevent problems if the device is reset
										; durring startup while auto sensing is
										; off.
										;
				lda		#$00b0
				sta		auto_sense_data+\
						rqst_sens.addnl_sens_code
				bra		unit_state

				ENDIF

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

				IF			scsi_dtype = mcd_40		THEN

				cmp		#$0008			;**** test Code ****
				bne		@err_8			;**** test Code ****
@no_format		dec		|un_formatted	;**** test Code ****
				brl		@io_error		;**** test Code ****

@err_8			lda		auto_sense_data+\		;Was it a RESET?
						rqst_sens.addnl_sens_code
				and		#$00ff

				cmp		#$00a7			;Checking for $A7 (Load Error, UnFormatted)
				beq		@no_format

				cmp		#$0010			;Checking for $10 (ID CRC Medium Error)
				beq		@no_format

				cmp		#$0011			;Checking for $11 (Unrecovered Read Error)
				beq		@no_format

				cmp		#$0015			;Checking for $15 (Seek Position Error)
				beq		@no_format

				cmp		#$0019			;Checking for $19 (Defect List Error)
				beq		@no_format

				cmp		#$0031			;Checking for $31 (Medium Format corrupted)
				beq		@no_format

				cmp		#$0032			;Checking for $32 (No Defect Spare Location Avail.)
				beq		@no_format

				cmp		#$00a8			;Checking for $A8 (Media being mounted)
				beq		@do_switch

				cmp		#$00b0			;Checking for $B0 (NO MEDIA)
				bne		@chk_other
										;
										; No Media.  Mark the DIB Hard Ofline also.
										;
				ldy		#dib.dvcflag
				lda		[dib_ptr],y
				and		#dvc_online--\
						$ffff
				ora		#dvc_hardofl++\
						dvc_switch++\
						dvc_hard_sw
				sta		[dib_ptr],y

				brl		@off_line

@chk_other		cmp		#$0028			;Checking for $28 (Medium Changed), $29 (RESET)
				bne		@io_error

				ENDIF

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

											;
											; Rebuild the DIBs.
											;
@do_switch		jsr		test_unit_rdy		;Is it ready yet?
				bcc		@its_ready			;Yes.

											;**** test Code ****
				lda		|auto_sense_data+\
						rqst_sens.sense_key
				and		#$00ff
				cmp		#$0008
				bne		@not_err_8
@no_format2		dec		|un_formatted
				bra		@its_ready
;				brl		@io_error
											;**** test Code ****

@not_err_8		lda		auto_sense_data+\	;Is there media in the drive?
						rqst_sens.addnl_sens_code
				and		#$00ff

				cmp		#$00a7				;Checking for $A7 (Load Error, UnFormatted)
				beq		@no_format2

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

				IF			scsi_dtype = mcd_40		THEN

				cmp		#$0028				;Checking for $28 (Medium Changed)
				beq		@do_switch			;Yes.
				cmp		#$0029				;Checking for $29 (Power On/Reset)
				beq		@do_switch			;Yes.
				cmp		#$00A8				;Checking for $A8 (Cart Loading)
				beq		@do_switch			;Yes.
				cmp		#$00b0				;Checking for $B0 (NO MEDIA)
				bne		@no_format2			;No.



				ELSE

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

				cmp		#$00b0				;Checking for $B0 (NO MEDIA)
				bne		@do_switch			;No.
;				bcs		@do_switch			;*************************

				ENDIF

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


@its_ready

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

				IF			scsi_dtype <> mcd_40		THEN

				jsr		set_512_mode

				ENDIF

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

										;
										; Issue the READ CAPACITY Command.
										;
				jsr		read_capacity
				bcs		@over_0			;Was there an error?
										;
										; Get the Block Count (Stored
										; High >> Low.  Must be switched
										; to Low >> High).  This is the last
										; readable block number.  Add 1 to
										; it for comparison reasons.
										;
				lda		|block.count\
						+internal_buff\
						+2
				xba
				adc		#$0001
				sta		|t_dvc_blocks
				lda		|block.count\
						+internal_buff
				xba
				adc		#null
				sta		|t_dvc_blocks+2

@over_0			stz		|trash_it		;DO NOT TRASH THIS DISK.

				pei		<dib_ptr
				pei		<dib_ptr+2

				jsr		rebld_dibs		;Rebuild DIBs and Issue a DISK_SW for each

				tax
				pla
				sta		<dib_ptr+2
				pla
				sta		<dib_ptr
				txa

				php
										;
										; Restore the original Direct Page values.
										;
				jsr		set_our_dp
				plp
				bcc		@switch_error
				jsr		test_unit_rdy
				bcs		@io_error
										;
										; Disk Switched Error.
										;
@switch_error	lda		#drvr_dsk_swch
				sec
				rts
										;
										; Some kind of I/O Error.
										;
@io_error		lda		#drvr_io
				sec
				rts
										;
										; Are we really ready to go?  If the
										; dvc_hard_sw flag is set, then we
										; will clear it and report the device
										; as switched.
										;
@ready_to_go	ldy		#dib.dvcflag
				lda		[dib_ptr],y
				tax

				and		#dvc_hard_sw
				beq		@no_switch

				txa
				and		#dvc_hard_sw--\
						$ffff
				sta		[dib_ptr],y

@no_switch		lda		#null
				clc
@rts			rts
										;
										; Device is currently offline.
										;
@off_line		lda		#drvr_off_line
				sec
				rts

				EXPORT	un_formatted	;**** test Code ****
un_formatted	dc.w	null			;**** test Code ****

				ENDP

				EJECT

;*******************************************************
;
;	'test_unit_rdy'
;
;	Test the Target Device to see if it is ready to
;	accept our commands.  If the device responds with a
;	CHECK CONDITION, then it will be up to the caller of
;	this routine to find out why.  This is not a test
;	routine.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Error Code if any
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if Unit no ready.
;
;*******************************************************

				EXPORT	test_unit_rdy
test_unit_rdy	PROC
									;
									; Set Internal Command Flag
									;
				dec		|internal
									;
									; Set Parm Pointer.
									;
				lda		#@test_unit_p
				sta		<scsi_mdrvr
				lda		#^@test_unit_p
				sta		<scsi_mdrvr+2
									;
									; It's a Status Call.
									;
				lda		#scsit_stat
				sta		|call_type
									;
									; Issue the Call
									;
				jmp		|main_drvr
									;
									; Data for this call
									; TEST UNIT READY Command Packet
									;
@test_unit_p	dc.b	$00			;Command Number
				dc.b	$00			;SCSI Command Flags
				dcb.b	3,$00		;Reserved
				dc.b	$00			;Vendor Unique
				dcb.b	6,$00		;Resrved

				ENDP

				EJECT

;*******************************************************
;
;	'CHK_PLAY_MODE'
;
;	This command is used to tell if the device is
;	currently in Audio Play Mode.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Error Code if any
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set not in play mode
;
;*******************************************************

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

				IF		scsi_dtype = apple_cd\
				OR		scsi_dtype = changer		THEN

				EXPORT	chk_play_mode
chk_play_mode	PROC
										;
										; First, disable auto sensing.
										;
				ldy		#dib.rslt_ptr
				lda		[dib_ptr],y
				pha
				lda		#null
				sta		[dib_ptr],y

				ldy		#dib.rslt_ptr+2
				lda		[dib_ptr],y
				pha
				lda		#null
				sta		[dib_ptr],y
										;
										; Set Internal Command Flag
										;
				dec		|internal
										;
										; Set Parm length.
										;
				lda		#$0006
				sta		<rqst_cnt
				stz		<rqst_cnt+2
										;
										; Set Parm Pointer.
										;
				lda		#@chk_play
				sta		<scsi_mdrvr
				lda		#^@chk_play
				sta		<scsi_mdrvr+2
										;
										; Set Buff Pointer.
										;
				lda		#@play_data
				sta		<buff_ptr
				lda		#^@play_data
				sta		<buff_ptr+2
										;
										; It's a Status Call.
										;
				lda		#scsit_stat
				sta		|call_type
										;
										; Issue the Call
										;
				jsr		|main_drvr
				bcs		@restore_dp		;Carry is set.  Not play mode
										;
										; Check the Status.
										;
										; Status $00 = In play mode
										;		 $01 = Pause (Play Mode)
										;		 $02 = Muting (Play Mode)
										;
										;		 $03 = Play completed (No Play)
										;		 $04 = Error (No Play)
										;		 $05 = Play not requested.
										;
				lda		@play_data
				and		#$0007
				cmp		#$0003			; <3 = Carry Clear. =>3 = Carry set
										;
										; Restore the Direct Page Values.
										;
@restore_dp		php
				jsr		set_our_dp
				plp
										;
										; Restore Auto Sensing Pointer.
										;
				ldy		#dib.rslt_ptr+2
				pla
				sta		[dib_ptr],y

				ldy		#dib.rslt_ptr
				pla
				sta		[dib_ptr],y

				rts
										;
										; AUDIO STATUS Command Packet
										;
@chk_play		dc.b	$cc				;Command Number
				dc.b	null			;SCSI Command Flags
				dcb.b	10,null			;Reserved
										;
										; Data received for this call
										;
@play_data		dcb.b	6,null

				ENDP

				ENDIF

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

				EJECT

;*******************************************************
;
;	The REQUEST SENSE Call is used to request any
;	information from the target device that might tell
;	us something about the way that the last call
;	competed.  This call should always be issued if any
;	SCSI Command returns from the SCSI Manager with a
;	CHECK CONDITION in the status result field.  The
;	data returned will be kept until the next REQUEST
;	SENSE Command is issued.  A flag however will be set
;	if any other commands are received by the SCSI Driver
;	indicating that this data is outdated.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Error Code if any
;				|sense_data	=	Data returned by the device
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if Unit no ready.
;
;*******************************************************

				EXPORT	rqst_sense
rqst_sense		PROC
									;
									; Set Internal Command Flag
									;
				dec		|internal
									;
									; Set Parm Pointer.
									;
				lda		#@rqst_sens_p
				sta		<scsi_mdrvr
				lda		#^@rqst_sens_p
				sta		<scsi_mdrvr+2
									;
									; 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 our own buffer pointer
									; for this call.
									;
				lda		#sense_data
				sta		<buff_ptr
				lda		#^sense_data
				sta		<buff_ptr+2
									;
									; Set our own request count
									; for this call.
									;
				lda		#$0012
				sta		<rqst_cnt
				stz		<rqst_cnt+2
									;
									; It's a Status Call.
									;
				lda		#scsit_stat
				sta		|call_type
									;
									; Issue the Call
									;
				stz		@error
				jsr		|main_drvr
				bcc		@return_dp
									;
									; Save error code
									;
				sta		@error
									;
									; 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 resutl.
									;
				lda		@error
				cmp		#$0001
				rts
									;
									; Data for this call
									;
@error			dc.w	null
									;
									; REQUEST SENSE Command Packet
									;
@rqst_sens_p	dc.b	$03			;Command Number
				dc.b	$00			;SCSI Command Flags
				dcb.b	3,$00		;Reserved
				dc.b	$00			;Vendor Unique
				dcb.b	6,$00		;Reserved

				ENDP

				EJECT

;*******************************************************
;
;	'reserve_unit'
;
;	The RESERVE UNIT Call is used to reserve the target
;	device for use by this host only.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Error Code if any
;				|sense_data	=	Data returned by the device
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if Unit already reserved.
;
;*******************************************************

				EXPORT	reserve_unit
reserve_unit	PROC

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

				IF			scsi_dtype = scanner		THEN
									;
									; Set Internal Command Flag
									;
				dec		|internal
									;
									; Set Parm Pointer.
									;
				lda		#@rsrv_unit_p
				sta		<scsi_mdrvr
				lda		#^@rsrv_unit_p
				sta		<scsi_mdrvr+2
									;
									; It's a Control Call.
									;
				lda		#scsit_cont
				sta		|call_type
									;
									; Issue the Call
									;
				jmp		|main_drvr
									;
									; RESERVE UNIT Command Packet
									;
@rsrv_unit_p	dc.b	$16			;Command Number
				dc.b	$00			;SCSI Command Flags
				dcb.b	3,$00		;Reserved
				dc.b	$00			;Vendor Unique
				dcb.b	6,$00		;Reserved

				ELSE					;scsi_dtype = scanner

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

				lda		#null
				clc
				rts

				ENDIF					;scsi_dtype = scanner

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

				ENDP

				EJECT

;*******************************************************
;
;	'release_unit'
;
;	The RELEASE UNIT Call is used to release the target
;	device from use by this host only.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Error Code if any
;				|sense_data	=	Data returned by the device
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		None.
;
;*******************************************************

				EXPORT	release_unit
release_unit	PROC

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

				IF			scsi_dtype = scanner		THEN
									;
									; Set Internal Command Flag
									;
				dec		|internal
									;
									; Set Parm Pointer.
									;
				lda		#@rles_unit_p
				sta		<scsi_mdrvr
				lda		#^@rles_unit_p
				sta		<scsi_mdrvr+2
									;
									; It's a Control Call.
									;
				lda		#scsit_cont
				sta		|call_type
									;
									; Issue the Call
									;
				jmp		|main_drvr
									;
									; RELEASE UNIT Command Packet
									;
@rles_unit_p	dc.b	$17			;Command Number
				dc.b	$00			;SCSI Command Flags
				dcb.b	3,$00		;Reserved
				dc.b	$00			;Vendor Unique
				dcb.b	6,$00		;Reserved

				ELSE					;scsi_dtype = scanner

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

				lda		#null
				clc
				rts

				ENDIF					;scsi_dtype = scanner

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

				ENDP

				EJECT
			
;*******************************************************
;
;	'mode_sense'
;
;	This routine issues an MODE SENSE call to the device.
;		
;	Inputs:		<dib_ptr	=	DIB start			(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		None.
;
;*******************************************************

				EXPORT	mode_sense
mode_sense		PROC
										;
										; And our buffer
										;
				lda		#internal_buff
				sta		<buff_ptr
				lda		#^internal_buff
				sta		<buff_ptr+2
				bra		command_ptr

				EXPORT	mode_sense2
										;
										; And our buffer
										;
mode_sense2		lda		#sense_data
				sta		<buff_ptr
				lda		#^sense_data
				sta		<buff_ptr+2
										;
										; Tell the Main Driver where
										; our command structure resides.
										;
command_ptr		lda		#@start_mode
				sta		<scsi_mdrvr
				lda		#^@start_mode
				sta		<scsi_mdrvr+2
										;
										; And our length of $ff Bytes
										;
				lda		#$0014
				sta		<rqst_cnt
				stz		<rqst_cnt+2
										;
										; Initialize the Page code to 1
										;
				lda		#$0001
				sta		@page_code
										;
										; Set internal command flag
										;
@try_again		dec		|internal
										;
										; Set the Call Type and Issue the
										; MODE SENSE Command.
										;
				lda		#scsit_stat
				sta		|call_type
				jsr		|main_drvr
				bcc		@rts
				pha
				lda		@page_code
				beq		@pla
				pla
				stz		@page_code
				bra		@try_again
				
@pla			pla
@rts			rts
										;
										; Data for the MODE SENSE Command
										;
@start_mode		dc.b	$1a
				dc.b	$00
@page_code		dc.b	$01
				dcb.b	9,$00

				ENDP

				EJECT
			
;*******************************************************
;
;	'read_capacity'
;
;	This routine issues an READ CAPACITY call to the
;	device and then uses the data returned to fill in
;	some of the blank holes in the DIB.
;		
;	Inputs:		<dib_ptr	=	DIB start			(LONG)
;				Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Carry		=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		None.
;
;*******************************************************

				EXPORT	read_capacity
read_capacity	PROC

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

				IF			block_dvc = true\
				AND			character_dvc = false		THEN
										;
										; Set internal command flag
										;
				dec		|internal
										;
										; Tell the Main Driver where
										; our command structure resides.
										;
				lda		#@read_cap
				sta		<scsi_mdrvr
				lda		#^@read_cap
				sta		<scsi_mdrvr+2
										;
										; And our buffer
										;
				lda		#internal_buff
				sta		<buff_ptr
				lda		#^internal_buff
				sta		<buff_ptr+2
										;
										; And our length of $ff Bytes
										;
				lda		#$0008
				sta		<rqst_cnt
				stz		<rqst_cnt+2
										; Set the Call Type and Issue the
										; READ CAPACITY Command.
										;
				lda		#scsit_stat
				sta		|call_type
				jsr		|main_drvr
				rts
										;
										; Data for the READ CAPACITY Command
										;
@read_cap		dc.b	$25
				dcb.b	11,$00

				ELSE

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

				lda		#null
				clc
				rts

				ENDIF

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

				ENDP

				EJECT

;*******************************************************
;
;	'get_data_status'
;
;	The GET DATA STATUS Call is used to find out how
;	much data is available to be read from the Scanner.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Error Code if any
;				|sense_data	=	Data returned by the device
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		None.
;
;*******************************************************

				EXPORT	get_data_status
get_data_status	PROC

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

				IF			scsi_dtype = scanner		THEN
									;
									; Set Internal Command Flag
									;
				dec		|internal
									;
									; Set Parm Pointer.
									;
				lda		#@get_d_stat_p
				sta		<scsi_mdrvr
				lda		#^@get_d_stat_p
				sta		<scsi_mdrvr+2
									;
									; 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 our own buffer pointer
									; for this call.
									;
				lda		#sense_data
				sta		<buff_ptr
				lda		#^sense_data
				sta		<buff_ptr+2
									;
									; Set our own request count
									; for this call.
									;
				lda		#$000C
				sta		<rqst_cnt
				stz		<rqst_cnt+2
									;
									; It's a Status Call.
									;
				lda		#scsit_stat
				sta		|call_type
									;
									; Issue the Call
									;
				jsr		|main_drvr
									;
									; Save error code
									;
				sta		@error
									;
									; 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 resutl.
									;
				lda		@error
				cmp		#$0001
				rts
									;
									; Data for this call
									;
@error			dc.w	null
									;
									; GET DATA STATUS Command Packet
									;
@get_d_stat_p	dc.b	$34			;Command Number
				dc.b	$00			;SCSI Command Flags
				dcb.b	3,$00		;Reserved
				dc.b	$00			;Vendor Unique
				dcb.b	6,$00		;Reserved

				ELSE					;scsi_dtype = scanner

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

				lda		#null
				clc
				rts

				ENDIF					;scsi_dtype = scanner

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

				ENDP

				EJECT

;____Dvc_Controls____
;*******************************************************
;
;	'start_unit'
;
;	Issue a START UNIT Command to the target device
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Error Code if any
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		None
;
;*******************************************************

				EXPORT	start_unit
start_unit		PROC

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

				IF			scsi_dtype = direct_acc	THEN

				ldy		#dib.dvcchar
				lda		[dib_ptr],y
				and		#removable
				bne		@do_it
				rts

@do_it

				ELSE

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

										;
										; Set Stop Flag
										;
				lda		#$0001
				tsb		|stop_bit
				bra		start_stop

				ENDIF

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

				EJECT

;*******************************************************
;
;	'stop_unit'
;
;	Issue a STOP UNIT Command to the target device
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Error Code if any
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		None
;
;*******************************************************

				EXPORT	stop_unit
stop_unit

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

				IF			scsi_dtype = direct_acc	THEN

				ldy		#dib.dvcchar
				lda		[dib_ptr],y
				and		#removable
				bne		@do_it
				rts

@do_it

				ELSE

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

									;
									; Set Stop Flag
									;
				lda		#$0001
				trb		|stop_bit
									;
									; Secondary entry point for START_UNIT
									; Command
									;
				EXPORT	start_stop
start_stop
									;
									; Set Internal Command Flag
									;
				dec		|internal
									;
									; Set Parm Pointer.
									;
				lda		#@start_stop_p
				sta		<scsi_mdrvr
				lda		#^@start_stop_p
				sta		<scsi_mdrvr+2

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

				IF			scsi_dtype = mcd_40	THEN

									;
									; Set IMMED Bit
									;
				lda		#immed_load

				ELSE

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

									;
									; Set IMMED Bit
									;
				lda		#immed_loc

				ENDIF

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

				tsb		@immed_loc
									;
									; It's a Control Call.
									;
				lda		#scsit_cont
				sta		|call_type
									;
									; Issue the Call
									;
				jmp		|main_drvr
									;
									; Data for this call
									; TEST UNIT READY Command Packet
									;
@start_stop_p	dc.b	$1B			;Command Number
@immed_loc		dc.b	$00			;SCSI Command Flags
				dcb.b	2,$00		;Reserved
				EXPORT	stop_bit
stop_bit		dc.b	$00			;Start/Stop Bit
				dc.b	$00			;Vendor Unique
				dcb.b	6,$00		;Resrved

				ENDIF

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

				ENDP

				EJECT

;*******************************************************
;
;	'SET 512 MODE'
;
;	This command is used to tell the device to set it's
;	block size to 512 mode.  This is used for the CD-ROM
;	Drives as well as any others that may need this
;	function.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Error Code if any
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if call failed
;
;*******************************************************

				EXPORT	set_512_mode
set_512_mode	PROC

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

				IF			block_dvc = true\
				AND			character_dvc = false		THEN

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

				IF			scsi_dtype = mcd_40	THEN

				clc
				rts

				ELSE					;scsi_dtype = mcd_40	THEN

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

										;
										; Set try twice flag.
										;
				dec		@try_twice
										;
										; Set Internal Command Flag
										;
				dec		|internal
										;
										; Set Parm length.
										;
				lda		#end_4_512-\
						data_4_512
				sta		<rqst_cnt
				stz		<rqst_cnt+2
										;
										; Set Parm Pointer.
										;
@try_again		lda		#@set_512_p
				sta		<scsi_mdrvr
				lda		#^@set_512_p
				sta		<scsi_mdrvr+2
										;
										; Set Parm Pointer.
										;
				lda		#data_4_512
				sta		<buff_ptr
				lda		#^data_4_512
				sta		<buff_ptr+2
										;
										; It's a Control Call.
										;
				lda		#scsit_cont
				sta		|call_type
										;
										; Issue the Call
										;
				jsr		|main_drvr
				bcc		@alles_klar
										;
										; Set Parm length.
										;
				lda		#$0000000c
				sta		<rqst_cnt
				stz		<rqst_cnt+2

				inc		@try_twice
				beq		@try_again
										;
										; Restore the values we destroyed
										;
@alles_klar		stz		@try_twice
				php
				pha
				jsr		set_our_dp
				pla
				plp
				rts
										;
										; Data for this call
										;
@try_twice		dc.w	null
										;
										; TEST UNIT READY Command Packet
										;
@set_512_p		dc.b	$15				;Command Number

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

				IF		scsi_dtype = direct_acc	THEN
										;
										; Set bit to save these values
										;
				dc.b	$01

				ENDIF					;scsi_dtype = direct_acc

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

				IF		scsi_dtype = apple_cd\
				OR		scsi_dtype = changer		THEN
										;
										; Clear bit to save these values
										;
				dc.b	$00

				ENDIF					;scsi_dtype = apple_cd or changer

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

				dcb.b	3,$00			;Reserved
				dc.b	$00				;Vendor Unique
				dcb.b	6,$00			;Resrved
										;
										; Data sent durring this call
										;
data_4_512		dc.b	null
				dc.b	$00
				dc.b	null
				dc.b	$08
										;number of blocks
				dc.b	null
				dc.b	$00
				dc.b	$00
				dc.b	$00
										;block size
				dc.b	null
				dc.b	$00
				dc.b	$02
				dc.b	$00

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

				IF		scsi_dtype = apple_cd\
				OR		scsi_dtype = changer		THEN

end_4_512

				EXPORT		select_page_num
select_page_num	dc.b	$01				;Page Number
				dc.b	$06				;Page Length
				dc.b	$e4				;Error Correction Flags
				dc.b	$03				;Retry Count
				dc.b	$00				;Correction Span
				dc.b	$00				;Head Offset Count
				dc.b	$00				;Data Strobe Offset
				dc.b	$00				;Recovery Time Limit

				ELSE					;scsi_dtype = apple_cd or changer

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

										;Page 1 Data
				EXPORT		select_page_num
select_page_num	dc.b	$01				;Page Number
				dc.b	$06				;Page Length
				dc.b	$e4				;Error Correction Flags
				dc.b	$03				;Retry Count
				dc.b	$00				;Correction Span
				dc.b	$00				;Head Offset Count
				dc.b	$00				;Data Strobe Offset
				dc.b	$00				;Recovery Time Limit
end_4_512

				ENDIF					;scsi_dtype = apple_cd or changer

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

				ENDIF					;scsi_dtype = mcd_40

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

				ELSE					;block_dvc = true

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

				lda		#null
				clc
				rts

				ENDIF					;block_dvc = true

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

				ENDP

				EJECT

;____Evnt_Support____
;*******************************************************
;
;	'notify_me'
;
;	This routine gets called by the SCSI Manager when
;	some event occurs that is out of the ordinary.
;
;	Inputs:		None Yet
;
;	Outputs:	All Regs	=	Scrambled
;
;	Errors:		None Yet
;
;*******************************************************

				EXPORT	notify_me
notify_me		PROC
										;
										; Vector for the SCSI Manager
										; to call when an event occurs
										; that affects my DIB
										; structures.  This could be a
										; new device online or media
										; insertion.
										;
				clc						;Add CODE LATER
				rtl

				ENDP

				EJECT

;____Misc_Support____
;*******************************************************
;
;	SET_DISK_SW
;
;	This routine checks the validity of the partition
;	map block currently loaded in the internal buffer.
;	This need only be called once per device.  If the
;	map is valid, then the carry will be clear.  The 'Z'
;	flag will also be set if this is not the first time
;	that this routine was called for this device.  If
;	the carry is set, then the map was invalid and the
;	block count at 'T_DVC_BLOCKS' has been placed in the
;	DIB pointed to by 'DIB_PTR'.  The starting block will
;	also have been set to null.
;
;	Inputs:		<DIB_PTR		=	DIB Location
;				Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Outputs:	Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;								=	Z = 0 if routine called before
;								=	C = 1 if not a partition map
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Errors:		Only those allowed by the System Service
;				Call SET_DISKSW.
;
;*******************************************************

				EXPORT	set_disk_sw
set_disk_sw		PROC

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

				IF			block_dvc = true\
				AND			character_dvc = false		THEN
										;
										; Check the removable flag.  If the media
										; is removable, then we will only set the
										; switch flag and let the status calls do
										; their thing before this device goes
										; online.  If it is non-removable, then
										; we need to say that the device is
										; switched, but also set the online bits.
										;
				ldy		#dib.dvcchar
				lda		[dib_ptr],y
				and		#removable
				beq		@non_removable
										;
										; Set the Disk Switched Bit in Status.
										;
				ldy		#dib.dvcflag
				lda		[dib_ptr],y
				ora		#dvc_switch
				sta		[dib_ptr],y
				bra		@over1
										;
										; Set the Disk Online Bit in Status.
										;
@non_removable	lda		leave_switched	; *** MSG 12/12/91 ***
				bmi		@over1			; *** MSG 12/12/91 ***

				ldy		#dib.dvcflag	; *** MSG 12/12/91 ***
;@non_removable	ldy		#dib.dvcflag	; *** MSG 12/12/91 ***
				lda		[dib_ptr],y
				and		#$ffff--\
						dvc_switch--\
						dvc_hardofl
				ora		#dvc_online
				sta		[dib_ptr],y
										;
										; Preserve the World from this.
										;
@over1			ldx		|gsos_dpage

				lda		>dev_num,x
				pha
				lda		>dib_ptr,x
				pha
				lda		>dib_ptr+2,x
				pha
				lda		<dev_num
				sta		>dev_num,x
				lda		<dib_ptr
				sta		>dib_ptr,x
				lda		<dib_ptr+2
				sta		>dib_ptr+2,x

				txa
				tcd

				jsl		set_disksw
				tay

				lda		|direct_page
				tcd

				ldx		|gsos_dpage
				pla
				sta		>dib_ptr+2,x
				pla
				sta		>dib_ptr,x
				pla
				sta		>dev_num,x

				stz		leave_switched	;Clear this flag *** MSG 12/12/91 ***

				tya
				cmp		#$0001
				rts
										;
										; Data for this call
										;
				EXPORT	leave_switched
leave_switched	dc.w	null			; *** MSG 12/12/91 ***

				ELSE

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

				lda		#null
				clc
				rts

				ENDIF

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

				ENDP

				EJECT

;*******************************************************
;
;	TRASH_VOLUME
;
;	This routine write a non-formatted pattern to block
;	2 of the volume whose DIB is [dib_ptr].  This is to
;	force the OS to re-format or lay down the OS data for
;	that volume rather than using what is already there
;	and appears to be valid.
;
;	Inputs:		<DIB_PTR		=	DIB Location
;				|trash_it		=	Boolean
;							0	=	Trash the Volume
;				Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Outputs:	Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	Ours
;				Data Bank		=	Ours
;
;	Errors:		Better be none.
;
;*******************************************************

				EXPORT	trash_volume
trash_volume	PROC

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

				IF			scsi_dtype = direct_acc	THEN

										;
										; Should we really trash the volume?
										;
				lda		|trash_it
				beq		@dont_trash
										;
										; Set Main Driver Pointer to
										; our data for the command.
										;
				lda		#@cmd_$800A
				sta		<scsi_mdrvr
				lda		#^@cmd_$800A
				sta		<scsi_mdrvr+2
										;
										; Set our buffer Pointer to
										; our Code for the data for
										; the command.
										;
				lda		#trash_volume
				sta		<buff_ptr
				lda		#^trash_volume
				sta		<buff_ptr+2
										;
										; Set length of our data for the
										; command.
										;
				lda		#block_size
				sta		<rqst_cnt
				stz		<rqst_cnt+2
										;
										; Call Main Driver
										;
				lda		#scsit_cont
				sta		|call_type
										;
										; Issue the call.
										;
				jsr		check_532_rw

@dont_trash		rts

										;
										; Command Data for this call.
										;
@cmd_$800A		dc.b	$0A
				dc.b	$00
				dc.b	$00
				dc.b	$02
				dc.b	$01
				dcb.b	7,$00

				ELSE

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

				lda		#null
				clc
				rts

				ENDIF

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

				ENDP

				EJECT

;*******************************************************
;
;	'SAVE_DP'
;
;	This routine is only called durring the initial
;	startup.  It saves away the current values on GS/OS
;	Direct Page so that we can use the space until we
;	have our own Direct Page.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	GS/OS
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	GS/OS
;				Data Bank	=	Ours
;
;	Errors:		None.
;
;*******************************************************

				EXPORT	save_dp
save_dp			PROC
											;
											; Check to see if this call was
											; nested.
											;
				lda		|valid
				bne		@exit
											;
											; Calculate Source Address.
											;
				clc
				lda		|gsos_dpage
				adc		#start_our_zp
				pea		$0000
				pha
											;
											; Destination Address of temporary
											; storage
											;
				pushlong	#saved_zp
				
				pushlong	#end_our_zp-\	;Length of the move
							start_our_zp
				
				pushword	#move_sinc_dinc

				jsl		move_info			;Move the data

				dec		|valid

@exit			rts

				ENDP

				EJECT

;*******************************************************
;
;	Move the contents of the first $20 bytes of GS/OS
;	Direct Page to our Direct Page.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if Unit is not the only
;				device in this linke that is still
;				online.
;
;*******************************************************

				EXPORT	restore_dp
restore_dp		PROC
											;
											; Check to see if this call was
											; nested.
											;
				lda		|valid
				beq		@exit
											;
											; Source Address of temporary
											; storage
											;
				pushlong	#saved_zp
											;
											; Calculate Destination Address.
											;
				clc
				lda		|gsos_dpage
				adc		#start_our_zp
				pea		$0000
				pha
				
				pushlong	#end_our_zp-\	;Length of the move
							start_our_zp
				
				pushword	#move_sinc_dinc

				jsl		move_info			;Move the data

				stz		|valid

@exit			rts

				ENDP

				EJECT

;*******************************************************
;
;	Move the contents of the first $20 bytes of GS/OS
;	Direct Page to our Direct Page.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Errors:		Carry set if Unit is not the only
;				device in this linke that is still
;				online.
;
;*******************************************************

				EXPORT	set_our_dp
set_our_dp		PROC
										;
										; Calculate Source Address.
										;
				clc
				lda		|gsos_dpage
				adc		#dev_num
				pea		$0000
				pha
										;
										; Calculate Destination Address of
										; our Direct Page that we want.
										;
				clc
				lda		|direct_page
				adc		#dev_num
				pea		$0000
				pha
				
				pushlong	#dib_ptr+4	;Length of the move
				
				pushword	#move_sinc_dinc

				jsl		move_info		;Move the data
				rts
				ENDP

				EJECT

;*******************************************************
;
;	'check_532_rw'
;
;	There are a few drives that have been formated with
;	512 ($214) bytes per block.  These devices work
;	perfectly fine in single block I/O, padding the end
;	with 20 bytes in a write, or stripping them for a
;	read, but when doing a multi-block transaction we
;	need to account for the 20 bytes between the end of
;	real data for this block and the beginning of the
;	next block.  Because of this, we will need to build
;	a data chaining structure that will read or write
;	512 bytes of data to the users buffer, followed by
;	20 bytes read or written to ROM space.  Doing this
;	will hinder performance and will prevent caching
;	from working.  If these are important to the user,
;	they can reformat their drive.
;
;	This routine is called in place of calling the Main
;	Driver.  It decides if the call should go via the
;	normal or 532 method.  All Inputs, Outputs and Setup
;	Structures are the same.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	GS/OS Direct Page
;				Data Bank	=	Ours
;
;	Errors:		None
;
;*******************************************************

				EXPORT	check_532_rw
check_532_rw	PROC

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

				IF			scsi_dtype = direct_acc	THEN

										;
										; Check for 532 byte block.
										;
				ldy		#dib.blksize
				lda		[dib_ptr],y
				cmp		#$214
				beq		@do_532
				jmp		|main_drvr

@do_532			jmp		munge_532

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

				ELSE

				jmp		|main_drvr

				ENDIF

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

				ENDP

				EJECT

;*******************************************************
;
;	'munge_532'
;
;	There are a few drives that have been formated with
;	512 ($214) bytes per block.  These devices work
;	perfectly fine in single block I/O, padding the end
;	with 20 bytes in a write, or stripping them for a
;	read, but when doing a multi-block transaction we
;	need to account for the 20 bytes between the end of
;	real data for this block and the beginning of the
;	next block.  Because of this, we will need to build
;	a data chaining structure that will read or write
;	512 bytes of data to the users buffer, followed by
;	20 bytes read or written to ROM space.  Doing this
;	will hinder performance and will prevent caching
;	from working.  If these are important to the user,
;	they can reformat their drive.
;
;	This routine is called in place of calling the Main
;	Driver.  All Inputs, Outputs and Setup Structures
;	are the same.
;
;	Inputs:		Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	Ours
;				Data Bank	=	Ours
;
;	Outputs:	Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	GS/OS Direct Page
;				Data Bank	=	Ours
;
;	Errors:		None
;
;*******************************************************

				EXPORT	munge_532
munge_532		PROC

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

				IF			scsi_dtype = direct_acc	THEN

										;
										; Set User's buffer location
										;
				lda		<buff_ptr
				sta		|@User_buff
				lda		<buff_ptr+2
				sta		|@User_buff+2
										;
										; Generate a block count.
										;
				stz		@block_cnt
				lda		<rqst_cnt+2
				sta		@block_cnt+1
				lda		<rqst_cnt
				and		#$fe00
				cmp		<rqst_cnt
				beq		@equal_512

				lda		#drvr_bad_cnt
				sec
				rts

@equal_512		lsr		@block_cnt+1
				ror		a
				tsb		@block_cnt-1
										;
										; Adjust users request cnt to reflect
										; that there are 20 bytes/block more
										; to transfer than the user requested.
										; This is done by multiplying the block
										; count that we just calculated bu $14.
										;
										; First preserve the old value.
										;
				lda		<rqst_cnt+2
				sta		@rqst_cnt+2
				lda		<rqst_cnt
				sta		@rqst_cnt
										;
										; Now adjust count.
										;
				lda		@block_cnt+2
				sta		|scratch0+2
				lda		@block_cnt
				sta		|scratch0
										;
										; x4 first
										;
				asl		|scratch0		;x2
				asl		|scratch0+2

				asl		|scratch0		;x2 (x4)
				asl		|scratch0+2
										;
										; Save the x4.
										;
				lda		|scratch0
				sta		|scratch2
				lda		|scratch0+2
				sta		|scratch2+2
										;
										; Finish the x16
										;
				asl		|scratch0		;x2 (x8)
				asl		|scratch0+2

				asl		|scratch0		;x2 (x16)
				asl		|scratch0+2
										;
										; Add the x4 to x16 for x20
										;
				clc
				lda		|scratch0
				adc		|scratch2
				sta		|scratch0
				lda		|scratch0+2
				adc		|scratch2+2
				sta		|scratch0+2
										;
										; Add the x20 to the original
										; request count.
										;
				clc
				lda		|scratch0
				adc		<rqst_cnt
				sta		<rqst_cnt
				lda		|scratch0+2
				adc		<rqst_cnt+2
				sta		<rqst_cnt+2
										;
										; Drop the Loop Counter by 1.
										;
				sec
				lda		@block_cnt
				sbc		#$0001
				sta		@block_cnt
				lda		@block_cnt+2
				sbc		#$0000
				sta		@block_cnt+2
										;
										; Preserve the data pointer currently
										; used in the DIB and replace it with
										; a pointer to our 532 byte block
										; Data Chaining instructions.
										;
				clc
				ldy		#dib.trx_ptr
				lda		[dib_ptr],y
				sta		|dib_data_struct

				lda		#@munger_DC
				sta		[dib_ptr],y

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

				lda		#^@munger_DC
				sta		[dib_ptr],y
										;
										; Preserve the Block size and replace
										; it with $0214
										;
				lda		<blk_size
				sta		@blk_size
				lda		#$0214
				sta		<blk_size
										;
										; Issue the call.
										;
				jsr		|main_drvr
										;
										; Preserve the result.
										;
				pha
				php
										;
										; Restore the Block size.
										;
				lda		@blk_size
				sta		<blk_size
										;
										; Restore DIB's data pointer.
										;

				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
										;
										; Restore the original request count.
										;
				lda		@rqst_cnt+2
				sta		<rqst_cnt+2
				lda		@rqst_cnt
				sta		<rqst_cnt
										;
										; Restore state of the call's exit.
										;
				plp
				pla
										;
										; Exit.
										;
				rts
										;
										; Temp Storage area.
										;
@rqst_cnt		dc.l	null
@blk_size		dc.w	null

@munger_DC								;
										; Data Chaining Structure.
										;
@User_buff		dc.l	$00000000		;Users Buffer Space
				dc.l	$00000200		;Request Count
				dc.l	$00000200		;Add to buffer at each pass
				dc.l	$00000000		;Reserved.

				dc.l	$00ff0600		;Location for spare bytes (ROM)
				dc.l	$00000014		;Number of bogus bytes
				dc.l	$00000000		;Leave buffer pointer alone
				dc.l	$00000000		;Reserved.

				dc.l	$ffffffff		;Looping Command
@block_cnt		dc.l	$00000000		;Block Count
				dc.l	$00000000-2		;Go Back 2 commands
				dc.l	$00000000		;Reserved

				dc.l	$00000000		;DCStop Command
				dc.l	$00000000		;DCStop Command
				dc.l	$00000000		;DCStop Command
				dc.l	$00000000		;DCStop Command

				dc.l	$00000000		;Safety space

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

				ELSE

				lda		#null
				clc
				rts

				ENDIF

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

				ENDP

				EJECT

;*******************************************************
;
;	'divide'
;
;	This routine divides a 16 bit number stored in
;	'divsor' into the 32 bit number stored in 'divend'.
;	The result will be in 'result' if no error occured.
;	If 'divend' is not an integral multiple of 'divsor'
;	an error will be returned.
;
;	Inputs:		'divsor'		=	16 bit number
;				'divend'		=	32 bit number
;				'max_blk_cnt'	=	Max Result Allowed
;				Acc				=	Unspecified
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	GS/OS Direct Page
;				Data Bank		=	Ours
;
;	Outputs:	'result'		=	result of division
;				Acc				=	Error Code if Carry Set
;				'max_blk_cnt'	=	null if no error
;									Intact if an error is returned
;				Y register		=	Unspecified
;				X register		=	Unspecified
;				P register		=	0=M=X=e
;				Direct Page		=	GS/OS Direct Page
;				Data Bank		=	Ours
;
;	Errors:		Carry set if non-integral result
;
;*******************************************************

				EXPORT	divide
divide			PROC

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

				IF			block_dvc = true\
				AND			character_dvc = false		THEN
												;
												; Clear Result field
												;
				stz		|result
				stz		|result+2
												;
												; 32 bits in result.
												;
				ldx		#32-1
												;
												; Make 'divsor' an odd value.
												; This is nothing more than
												; deviding both numbers by 16
												; until 'divsor' is an odd value.
												; If any bits roll out of 'divend'
												; then this was an incorrect call.
												;
@div16_loop		lda		|divsor
				cmp		#block_size				;Special Case /block_size
				bne		@not_512
				lda		|divend
				and		#block_size-1
				bne		@error

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

				IF			scsi_dtype = direct_acc	THEN
												;
												; Quick Divide by block_size
												;
				lda		|divend+3
				and		#$00ff
				lsr		a
				sta		|result+2

				lda		|divend+1
				ror		a
				sta		|result

				jmp		@chk_rslt

				ENDIF

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

				IF			scsi_dtype = apple_cd\
				OR			scsi_dtype = changer		THEN
												;
												; Quick Divide by $0200
												;
				lda		|divend+3
				and		#$00ff
				lsr		a
				sta		|result+2

				lda		|divend+1
				ror		a
				sta		|result

				jmp		@chk_rslt

				ENDIF

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

				IF			scsi_dtype = mcd_40	THEN
												;
												; Quick Divide by $2000
												;
				stz		|result
				stz		|result+2
				lda		|divend+2
				sta		|result

				lda		|divend					;$2000
				asl		a						;$4000
				rol		|result					;x2
				rol		|result+2				;x2

				asl		a						;$8000
				rol		|result					;x4
				rol		|result+2				;x4

				asl		a						;$0000
				rol		|result					;x8
				rol		|result+2				;x8

				jmp		@chk_rslt

				ENDIF

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

@not_512		and		#bit_0
				bne		@do_divide
				lsr		|divsor
				lsr		|divend+2
				ror		|divend
				bcc		@div16_loop
				bra		@error
												;
												; Main division loop.  We will
												; continue to divide the divsor
												; by 16 until finished.  Each
												; time that divsor becomes odd,
												; we will subtract divend and
												; roll a bit into the high end
												; of result.
												;
@do_divide		lda		|divend
				and		#bit_0
				clc								;We must clear the carry incase
				beq		@do_div16				;the branch is taken.
												;
												; We are odd. Do the subtraction.
												;
				sec
				lda		|divend
				sbc		|divsor
				sta		|divend
				lda		|divend+2
				sbc		#$0000
				sta		|divend+2
				bcc		@error					;If this is taken, bad data.
												;
												; Divide both the divsor and 
												; results by 16.  The carry will
												; roll into the reult from the
												; high bit end.
												;
@do_div16		php
				lsr		|divend+2
				ror		|divend
				plp
@clean_up		ror		|result+2
				ror		|result
												;
												; Have we done 32 bits yet?
												;
				dex
				bmi		@chk_rslt				;Yes.
												;
												; No we havent.  If divsor is
												; non-zero then do next itteration.
												; If it is = zero then clean up
												; result and exit.
												;
				lda		|divend+2
				ora		|divend
				bne		@do_divide
												;
												; Clean up Result.
												;
				clc
				bra		@clean_up
												;
												; Here for bcs offset.
												;
@error			sec
				lda		#drvr_bad_cnt
				rts
												;
												; Check to see if the result is within
												; the max.
												;
@chk_rslt		lda		|max_blk_cnt
				ora		|max_blk_cnt+2
				beq		@out

				sec
				lda		|max_blk_cnt
				sbc		|result
				sta		@temp
				lda		|max_blk_cnt+2
				sbc		|result+2
				bge		@clean
												;
												; Exit
												;
				sec
				lda		#drvr_bad_parm
				rts

@clean			stz		|max_blk_cnt
				stz		|max_blk_cnt+2
@out			clc
				rts
													;
													; Data Area.
													;
@temp			dc.w	null

				ELSE

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

				lda		#null
				clc
				rts

				ENDIF

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

				ENDP

				EJECT

;*******************************************************
;
;	'calc_bytes'
;
;	This routine Multiplies a 16 bit number stored in
;	'm_blk_size' by a 32 bit number stored in 'm_blk_cnt'.
;	The result will be in 'm_rslt'.
;
;	Inputs:		'm_blk_size'=	16 bit Block Size
;				'm_blk_cnt'	=	32 bit Block Count
;				Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	GS/OS Direct Page
;				Data Bank	=	Ours
;
;	Outputs:	'm_rslt'	=	result of division
;				Acc			=	Unspecified
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	GS/OS Direct Page
;				Data Bank	=	Ours
;
;	Errors:		None
;
;*******************************************************

				EXPORT	calc_bytes
calc_bytes		PROC

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

				IF			block_dvc = true\
				AND			character_dvc = false		THEN
											;
											; Clear Result field
											;
				stz		|m_rslt
				stz		|m_rslt+2
											;
											; Check 'm_blk_size' against the block
											; size for this device.  If it is Equal,
											; the do cheap processing.  Otherwise do
											; full multiply logic.
											;
@div16_loop		lda		|m_blk_size
				cmp		#block_size			;Special Case *block_size
				bne		@not_512

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

				IF			scsi_dtype = direct_acc	THEN
											;
											; Quick Multiply by $0200
											;
				lda		|m_blk_cnt
				asl		a
				sta		|m_rslt+1

				lda		|m_blk_cnt+2
				and		#$007f
				rol		a
				ora		|m_rslt+3			;Watch out for that +4 value.
				sta		|m_rslt+3			;It doesn't belong to us.

				jmp		@clean

				ENDIF

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

				IF			scsi_dtype = apple_cd\
				OR			scsi_dtype = changer		THEN
											;
											; Quick Multiply by $0200
											;
				lda		|m_blk_cnt
				asl		a
				sta		|m_rslt+1

				lda		|m_blk_cnt+2
				and		#$007f
				rol		a
				ora		|m_rslt+3			;Watch out for that +4 value.
				sta		|m_rslt+3			;It doesn't belong to us.

				jmp		@clean

				ENDIF

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

				IF			scsi_dtype = mcd_40	THEN
											;
											; Quick Multiply by $2000
											;
				lda		|m_blk_cnt
				sta		|m_rslt+2			;Cheap * $10000

				lda		|m_blk_cnt+2
				lsr		a
				ror		|m_rslt+2			;Cheap * $8000 ( * $10000/2 )
				ror		|m_rslt				;Cheap * $8000 ( * $10000/2 )

				lsr		a
				ror		|m_rslt+2			;Cheap * $4000 ( * $10000/4 )
				ror		|m_rslt				;Cheap * $4000 ( * $10000/4 )

				lsr		a
				ror		|m_rslt+2			;Cheap * $2000 ( * $10000/8 )
				ror		|m_rslt				;Cheap * $2000 ( * $10000/8 )

				jmp		@clean

				ENDIF

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

@not_512
											;
											; Actual Multiply Loop.
											;
				lsr		|m_blk_size
				bcc		@shift_cnt			;Don't Add this one.
											;
											; Add in Current Byte Count.
				clc
				lda		|m_rslt
				adc		|m_blk_cnt
				sta		|m_rslt
				lda		|m_rslt+2
				adc		|m_blk_cnt+2
				sta		|m_rslt+2
											;
											; Multiply Count by 2.
											;
@shift_cnt		asl		|m_blk_cnt
				rol		|m_blk_cnt+2

				lda		|m_blk_size
				bne		@not_512			;only do till 0  ( <16 times )
											;
											; Clean Exit.
											;
@clean			clc
				rts

				ELSE

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

				lda		#null
				clc
				rts

				ENDIF

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

				ENDP

				EJECT

				END