
;*******************************************************
;
;	SCSI Driver 'Main Driver'.
;
;	Written by Matt Gulick.		Started May 25,1988
;
;	Copyright Apple Computer, Inc. 1988-90
;
;*******************************************************

;*******************************************************
;
;	This file contains the 'Main Driver' as defined in
;	the ERS.
;
;*******************************************************

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

;	May 25,		1988	File started.
;	June 6,		1988	Turned sections into subroutines
;						and wrote the Main Driver Proper.
;	June 20,	1988	Added Register Input and Output.
;						Also included the changes from
;						the code review.
;	Oct  25,	1988	Made changes in code to reflect the
;						redesign of the SCSI Manager interface.
;

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

					IMPORT		cmd_t_tbl
					IMPORT		call_type
					IMPORT		scratch0
					IMPORT		cmd_ps_tbl
					IMPORT		c_len_len
					IMPORT		c_blk_len
					IMPORT		divide
					IMPORT		result
					IMPORT		divend
					IMPORT		divsor
					IMPORT		time_cnt
					IMPORT		rout2_s_disp
					IMPORT		gsos_dpage
					IMPORT		f_partition
					IMPORT		internal
					IMPORT		disk_switch
					IMPORT		scsi_mgrnum
					IMPORT		manager_cmd
					IMPORT		cur_cmd
					IMPORT		cur_group
					IMPORT		trans_dest
					IMPORT		p_block_num
					IMPORT		killer_blk
					IMPORT		trans_flag
					IMPORT		temp_x
					IMPORT		temp_y
					IMPORT		timeout_flag
					IMPORT		auto_sense_data
					IMPORT		lst_rslt_stat
					IMPORT		lst_rslt_skey
					IMPORT		lst_rslt_info
					IMPORT		lst_rslt_rqlen
					IMPORT		lst_rslt_scode
					IMPORT		auto_sense_data
					IMPORT		auto_sense_data

					PRINT		OFF

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

					PRINT		ON

					EJECT

;*******************************************************
;
;	Main Entry point to the 'Main Driver'.
;
;	The function of this code is to take the callers
;	command data and build a SCSI Command Packet.  This
;	is done using the conversion tables in the file
;	'SCSI Command Table'.  The command and other data
;	is actually built in the space that has been added
;	to the DIB and then routed to the SCSI Manager.
;
;	The routine is called with the LONG pointer to the
;	callers command data in the Direct Page location
;	'scsi_mdrvr'.  This, in conjunction with the DIB Ptr
;	at direct page location 'dib_ptr' as the address
;	for the template overlay, will be used to do the
;	following:
;
;		Transfer the callers command data to the command
;		packet.
;			Zero all 12 bytes first.
;			Validate Internal Command flag if set.
;			.OR. in all values durring translation.
;			Adjust Transfer length if needed.
;			Convert Block Number from Logical to Actual
;				If partition Flag is ­ zero then leave
;				block number unmodified.
;			Check for command errors.
;
;		Stuff SCSI Manager Flags
;
;		Stuff data in the TRX buffer data structure
;
;		Ship the call out to the SCSI Manager.
;
;		Clear Partition Call Flag.
;
;		Clear the Internal Command Flag.
;
;		Translate any SCSI errors into GS/OS errors.
;
;	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 Error.
;
;*******************************************************

					EXPORT	main_drvr
main_drvr			PROC
											;
											; Clear the Timeout flag
											;
					stz		|timeout_flag
											;
											; Clear the Command Buffer
											;
					ldx		#max_cmd_len*2\
							-2
					ldy		#max_cmd_len*2\
							+dib.scsicmd\
							-2
					lda		#$0000
@loop				sta		[dib_ptr],y
					dey
					dey
					dex
					dex
					bpl		@loop
											;
											; Get the Current CoMmanD and GRouP
											;
					lda		[scsi_mdrvr]
					and		#$00ff
					sta		|cur_cmd		;Preserve Current Command Number
					pha
											;
											; Get Offsets to Validate Command
											; Support by the device.
											;
					tax
					and		#bit_4\
							+bit_5\
							+bit_6\
							+bit_7
					lsr		a
					lsr		a
					lsr		a
					tay
					lda		|bm_cmd_offset,y
					tay
					txa
					and		#bit_0\
							+bit_1\
							+bit_2\
							+bit_3
					asl		a
					tax
					lda		[dib_ptr],y
					and		|bm_cmd_mask,x
					bne		@cmd_support
											;
											; Command Not Supported
											; by this device.
											;
					pla							;Fix Stack
					lda		#drvr_bad_code
					sec
					rts
											;
											; Get the SCSI Group Number.
											;
@cmd_support		pla
					and		#bit_5\
							+bit_6\
							+bit_7
					lsr		a
					lsr		a
					lsr		a
					lsr		a
					lsr		a
					sta		|cur_group
											;
											; Set TRANSlation PoiNTer
											;
					jsr		s_trans_ptr
					bcs		@exit_far
											;
											; VALidate the CoMmanD TYPe
											;
					jsr		val_cmd_typ
					bcs		@exit_far		;Bad Command
											;
											; Set 'scsi_zp4' to scsi Command
											; in DIB
											;
					clc
					lda		<dib_ptr
					adc		#dib.scsicmd
					sta		<scsi_zp4
					lda		<dib_ptr+2
					adc		#$0000
					sta		<scsi_zp4+2
											;
											; TRANSLATE user's command data
											; into SCSI Command Packet
											;
					jsr		translate
											;
											; FINALIZE the SCSI Command Packet.
											; This includes updating the
											; requested block number to account
											; for partitioning, and the request
											; length from bytes to blocks.
											;
					jsr		finalize
@exit_far			bcs		@exit
					bvs		@no_data
											;
											; Stuff SCSI Manager flags.
											;
					jsr		stuff_flag
											;
											; Build the Transmit/Receive
											; structure.
											;
					lda		#scsi_io_call	;set SCSI Manager Command to
					sta		|manager_cmd	;I/O Call.

					ldy		#dib.trx_buff	;Stuff the Buffer Pointer
					lda		<buff_ptr
					sta		[dib_ptr],y

					ldy		#dib.trx_buff+2
					lda		<buff_ptr+2
					sta		[dib_ptr],y
											;
											; Stuff the request count in both the
											; Total length and in the DCMove command.
											;
					lda		<rqst_cnt

					ldy		#dib.trx_length	;Stuff the DCMove transfer count
					sta		[dib_ptr],y

					ldy		#dib.trx_rqst	;Stuff the Requested transfer count
					sta		[dib_ptr],y

					lda		<rqst_cnt+2

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

					ldy		#dib.trx_rqst+2
					sta		[dib_ptr],y
											;
											; Mark auto sense buffer as trashed.
											;
					stz		|auto_sense_data
					stz		|auto_sense_data+2
											;
											; Send command to the SCSI Manager
											;
					jsr		issue_cmd
											;
											; Place Info in for Get Last Result
											; Status Call.
											;
					php
					pha

					sta		|lst_rslt_stat
											;
											; Did we get auto sense data?
											;
					lda		|auto_sense_data
					ora		|auto_sense_data+2
					beq		@no_sense		;No.
											;
											; Yes we did.  Setup last result
											; info incase it is needed.
											;
					lda		|auto_sense_data+\
							rqst_sens.sense_key
					and		#$00ff
					sta		|lst_rslt_skey

					lda		|auto_sense_data+\
							rqst_sens.info_bytes
					sta		|lst_rslt_info
					lda		|auto_sense_data+\
							rqst_sens.info_bytes+2
					sta		|lst_rslt_info+2

					clc
					lda		|auto_sense_data+\
							rqst_sens.addnl_sens_len
					and		#$00ff
					adc		#$0008
					sta		|lst_rslt_rqlen

					lda		|auto_sense_data+\
							rqst_sens.addnl_sens_code
					and		#$00ff
					sta		|lst_rslt_scode
											;
											; Restore Acc. Carry and Stack.
											;
@no_sense			pla
					plp
											;
											; Clear partiton call and internal
											; command flags.
											;
@no_data			stz		|f_partition
					stz		|internal
											;
											; return to Driver front end.
											;
@exit				rts

;*******************************************************
;
; Transfer the callers command data to the command
; packet.
;
;*******************************************************

;*******************************************************
;*******************************************************
;
;	Subroutines from the Main Driver.
;
;*******************************************************
;*******************************************************

					EJECT
			
;*******************************************************
;
;	Point to Command Translation Table
;		'trans_flag'	=	Translation Flags
;		'scsi_zp2'		=	Translation Data.
;
;*******************************************************


s_trans_ptr			lda		#cmd_t_tbl
					sta		<scsi_zp2
										;
										; Find Command Translation Table for conversion
										;
@find_cmd			lda		(scsi_zp2)
					bmi		@chk_end
					cmp		|cur_cmd
					beq		@got_r_cmd
										;
										; Goto next entry.
										;
					clc
					lda		<scsi_zp2
					adc		#$0004
					sta		<scsi_zp2
					bra		@find_cmd
										;
										; Is it really the end of the table?
										;
@chk_end			cmp		#$ffff
					beq		@no_cmd_fnd
										;
										; The Command was not found.
										; Return a Bad Command error
										;
@no_cmd_fnd			lda		#drvr_bad_code
					sec
					rts
										;
										; We found our command.  Now
										; point to the translation data.
										;
@got_r_cmd			ldy		#$0002
					lda		(scsi_zp2),y
					sta		<scsi_zp2
										;
										; Now get the Translation Flags.
										;
					lda		(scsi_zp2)
					sta		|trans_flag
										;
										; Set Timeout Factor.
										;
@check_timer		and		#scsic_tout
					beq		@not_by_block

					dec		|timeout_flag

@not_by_block		ldy		#$0002
					lda		(scsi_zp2),y
					ldy		#dib.time_out
					sta		[dib_ptr],y
										;
										; Advance pointer to translation data
										;
					clc
					lda		<scsi_zp2
					adc		#$0004			;Setup Pointer to translation data
					sta		<scsi_zp2
					rts

					EJECT
			
;*******************************************************
;
;	Check to see if this is an internal command.  If it
;	is, then the high of 'internal' must be set.
;
;	Validate that this command is a Staus Command if we
;	were called with a Status type call.  Or is it a
;	Control call with a Control Command?
;
;*******************************************************


val_cmd_typ			stz		|disk_switch		;Ensure that this flag is clear.

					lda		|trans_flag
					tay
					and		#scsic_int
					beq		@non_int		;If = then Internal Command flag = 0
					bit		|internal		;non-0.  Is it an internal command?
					bpl		@no_cmd_fnd
											;
											; Is the command a Status if entry
											; was via status, or is it a Control
											; vial control entry.  If neither
											; then error out.
											;
@non_int			tya
					and		|call_type
					beq		@no_cmd_fnd		;No match.  Error.
											;
											; Must this command be issued to the
											; first DIB of a linked device?
											;
					tya
					and		#scsic_1st
					beq		@chk_dsksw
											;
											; Check Headlink Pointer.
											;
					phy
					ldy		#dib.headlnk
					lda		[dib_ptr],y
					beq		@ply
					ldy		#dib.devnum
					cmp		[dib_ptr],y
					beq		@ply
											;
											; The Command was invalid.
											; Return a Bad Command error
											;
					ply						;Restore the stack.
@bad_dev_num		lda		#bad_dev_number
					sec
					rts
											;
											; Check to see if we need to do a
											; DISK SWITCH after this command.
											;
@ply				ply

@chk_dsksw			tya
					and		#scsic_dsw
					beq		@clc

					dec		disk_switch

@clc				clc
					rts
											;
											; The Command was invalid.
											; Return a Bad Command error
											;
@no_cmd_fnd			lda		#drvr_bad_code
					sec
@rts				rts

					EJECT
			
;*******************************************************
;
;	Is it an ASIS Command, or does it need to be
;	translated?  What ever the case, when this command
;	returns, the command data will be in it's place
;	ready for the length and block fields to be updated
;	if needed.
;
;*******************************************************


translate			lda		|trans_flag
					and		#scsid_asis
					beq		@translate
												;
												; Yes. It is as is.  Move the Command
												; bytes over.
												;
					ldy		#max_cmd_len-2
@move_cmd			lda		[scsi_mdrvr],y
					sta		[scsi_zp4],y
					dey
					dey
					bpl		@move_cmd
@rts				rts							;Return to caller.
												;
												;
												; We have gotten to this piece of code
												; because the user data is not in the
												; form accepted by the SCSI Target.
												; We need to translate the data using
												; the translation data pointed to by
												; the direct page value 'scsi_zp2' It
												; is extremely important that we .OR.
												; in all values during translation.
												;
@translate			lda		#null				;Always do the Command Byte
					bra		@over

@loop				lda		(scsi_zp2)			;Get translation Data.
					beq		@rts				;we are at the end if value = $0000
												;
												; Place High Byte in Source Offset and
												; the Low Byte in Destination Offset.
												;
@over				tay
					and		#bit_0\
							+bit_1\
							+bit_2\
							+bit_3
					sta		|trans_dest
					tya
					xba							;Move High Byte to Low Byte
					and		#bit_0\
							+bit_1\
							+bit_2\
							+bit_3
					tay
												;
												; Move Command Data.
												;
					shortm						;We only want the byte from this
					lda		[scsi_mdrvr],y
					ldy		|trans_dest			;offset.
					ora		[scsi_zp4],y
					sta		[scsi_zp4],y
					longm
												;
												; Point to next Translation Byte
												;
					clc
					lda		<scsi_zp2
					adc		#$0002
					sta		<scsi_zp2
					bra		@loop

					EJECT
			
;*******************************************************
;
;	The command Packet is now built except for some
;	minor cosmetic work.
;
;*******************************************************
											;
											; Stuff the SCSI Device ID (Our unit
											; number) and our Slot Number into the
											; commad area for the SCSI Manager.
											;
finalize
					ldy		#dib.slotnum
					lda		[dib_ptr],y
					ldy		#dib.scsi_slot
					sta		[dib_ptr],y

					ldy		#dib.unitnum
					lda		[dib_ptr],y
					and		#max_p_mask\
							--$ffff
					ldy		#dib.scsiid
					sta		[dib_ptr],y
											;
											; Set ZP3 to point to the Command
											; Packet information.  This will be
											; used to place the length and
											; block number as well as the
											; command length for the SCSI
											; Manager.
											;
					lda		|cur_group
					asl		a				;Multiply by 6
					sta		|scratch0
					asl		a
					adc		|scratch0		;Carry Clear by asl and adc.
					adc		#cmd_ps_tbl		;Carry still Clear.
					sta		<scsi_zp3
											;
											; Adjust Transfer length if needed.
											;
					lda		trans_flag
					and		#scsid_blk++\	;Convert Trans Count to Blocks
							scsid_byte++\	;Leave Trans Count in Bytes
							scsid_none		;Transfer count does not go in CMD

					cmp		#scsid_none		;Is the Request length in the command?
					beq		@blknum_jmp		;No.
					cmp		#scsid_byte		;Yes.  Is it in Byte form?
					beq		@move_byte		;Yes.
					cmp		#scsid_blk		;No.  Is it in Block form?
					beq		@move_blk		;Yes.
					sec						;No. Return Bad Byte Count Error.
@bad_rqst			lda		#drvr_bad_cnt
@clv				clv						;Clear Overflow Flag
					rts

@blknum_jmp			jmp		@block_num		;No.
											;
											; If the request count = 0 then exit with
											; the overflow flag set.  This will indicate
											; that no data is to tbe transfered.  It
											; also indicates that this is not an error
											; condition.
											;
@zero_cnt			bit		@overflow
					clc
					rts

@overflow			dc.w	$4000
											;
											; Convert Request Count to Blocks.
											;
@move_blk			lda		<rqst_cnt		;Setup Divide Routine.
					sta		|divend

					ora		<rqst_cnt+2		;Check for zero request from block device.
					beq		@zero_cnt

					lda		<rqst_cnt+2
					sta		|divend+2

					ldy		#dib.blksize
					lda		[dib_ptr],y
					sta		|divsor

					jsr		divide
					bcs		@clv
											;
											; Get requested Length in blocks and
											; goto stuff routine. Also stuff for
											; timeout calculation if needed.
											;
					ldx		|result
					stx		|time_cnt
					ldy		|result+2
					sty		|time_cnt+2

					bra		@stuff_len
											;
											; Get requested length in bytes and goto
											; stuff routine.
											;
@move_byte			ldx		<rqst_cnt
					ldy		<rqst_cnt+2
											;
											; Clear timeout Values.
											;
					stz		|time_cnt
					stz		|time_cnt+2

;*******************************************************
;
;	Transfer request length to command packet.
;
;*******************************************************

@stuff_len			sty		|temp_y			;Save 32 bit transfer length for later
					stx		|temp_x
					short					;Goto 8 bit Acc
					ldy		#<c_len_len		;Get offset into command packet description
					lda		(scsi_zp3),y	;table and get Length of length field
					tax
											;
											; Check byte count so that any value greater
											; than the max will be represented by the
											; max.
											;
					lda		|temp_y+1
					beq		@byte_3_ok
					lda		#$ff
					stz		|temp_y+1
					sta		|temp_y
					sta		|temp_x+1
					sta		|temp_x
@byte_3_ok			cpx		#$03
					bge		@count_ok

					lda		|temp_y
					beq		@byte_2_ok
					lda		#$ff
					stz		|temp_y
					sta		|temp_x+1
					sta		|temp_x
@byte_2_ok			cpx		#$02
					beq		@count_ok

					lda		|temp_x+1
					beq		@byte_1_ok
					lda		#$ff
					stz		|temp_x+1
					sta		|temp_x
@byte_1_ok			cpx		#$01
					beq		@count_ok

					stz		|temp_x

@count_ok			dey
					lda		(scsi_zp3),y	;and the offset to the end of the length
					tay						;field
					lda		|temp_x			;Stuff the Low byte of the length
					ora		[scsi_zp4],y
					sta		[scsi_zp4],y
					dex
					beq		@exit_len		;Do Block Number.
					dey
					lda		|temp_x+1		;Stuff the Mid-Low byte of the
					ora		[scsi_zp4],y	;length if used.
					sta		[scsi_zp4],y
					dex
					beq		@exit_len		;Do Block Number.
					dey
					lda		|temp_y			;Stuff the Mid-High byte of the
					ora		[scsi_zp4],y	;length if used.
					sta		[scsi_zp4],y
					dex
					beq		@exit_len		;Do Block Number.
					dey
					lda		|temp_y+1		;Stuff the High byte of the
					ora		[scsi_zp4],y	;length if used.
					sta		[scsi_zp4],y
											;
											; Clean up Processor.
											;
@exit_len			longmx

;*******************************************************
;
;	Convert Block Number from Logical to Actual If
;	partition Flag is ­ zero then leave block number
;	unmodified.
;
;*******************************************************

@block_num			lda		|trans_flag		;Does it have a block number in the
					and		#scsit_blk		;command packet?
					beq		@exit_blk1		;No.
											;
											; Is this a partition Map call?
											;
					bit		|f_partition	;Get the partition call flag.
					bmi		@exit_blk1		;It's a partition map call.
											;
											; It has a block number and is not a
											; partition map command.  Translate
											; that logical block number to a
											; physical number.
											;
					ldy		#dib.start_blk+2
					lda		[dib_ptr],y
					sta		|p_block_num\	;Stored High È Low
							-dib.start_blk,y
					ldy		#dib.start_blk
					lda		[dib_ptr],y
					sta		|p_block_num\	;Stored High È Low
							-dib.start_blk,y
											;
											; Zero out starting block number space.
											; This space is used to retrieve the
											; starting block number for a call incase
											; it is needed.  An example of this is a
											; device specific write type call that is
											; going to update blocks that might be in
											; the cache.  If this happens, then this
											; will be used as a starting point from
											; whence to kill those blocks that are in
											; the cache.
											;
					stz		|killer_blk
					stz		|killer_blk+2
											;
											; We now have the block offset where we
											; need it.
											;
					short					;Goto 8 bit Acc
					ldy		#<c_blk_len		;Get offset into command packet description
					lda		(scsi_zp3),y	;table and get Length of block field
					tax
					dey
					lda		(scsi_zp3),y	;and the offset to the end of the block
					tay						;field
					clc
					lda		[scsi_zp4],y	;adjust the Low byte of block num.
					sta		|killer_blk
					adc		|p_block_num+3	;add the physical block offset.
					sta		[scsi_zp4],y
					dex
					beq		@exit_blk		;Do Block Number.
					dey
					lda		[scsi_zp4],y	;adjust the Mid-Low byte of block num.
					sta		|killer_blk+1
					adc		|p_block_num+2	;add the physical block offset.
					sta		[scsi_zp4],y
					dex
					beq		@exit_blk		;Do Block Number.
					dey
					lda		[scsi_zp4],y	;adjust the Mid-High byte of block num.
					sta		|killer_blk+2
					adc		|p_block_num+1		;add the physical block offset.
					sta		[scsi_zp4],y
					dex
					beq		@exit_blk			;Do Block Number.
					dey
					lda		[scsi_zp4],y		;adjust the High byte of block num.
					sta		|killer_blk+3
					adc		|p_block_num		;add the physical block offset.
					sta		[scsi_zp4],y
												;
												; Clean up Processor.
												;
@exit_blk			longmx
												;
												; Check for Block Number overflow
												; (Carry Set)
												;
					bcc		@exit_blk2
					lda		#drvr_bad_req
					rts
			
@exit_blk1			clc
@exit_blk2			clv							;Clear no data flag
					rts

					EJECT
			
;*******************************************************
;
;	Stuff Flags for SCSI Manager
;
;*******************************************************

stuff_flag			lda		#null

					ldy		#dib.scsic_flgs
					sta		[dib_ptr],y
					rts

					EJECT
			
;*******************************************************
;
;	Call the SCSI Manager VIA the Supervisory Dispatcher.
;
;*******************************************************
issue_cmd
												;
												; Set Timeout Factor.
												;
				lda		|timeout_flag
				beq		@over
												;
												; Adjust Time Out Value.
												;
				ldy		#dib.time_out
				lda		[dib_ptr],y
				sta		@time_mult

				lda		|time_cnt+2
				bne		@do_max_time
				lda		|time_cnt
				stz		|time_cnt

@mult_loop		lsr		@time_mult
				bcc		@shift

				php
				clc
				adc		|time_cnt
				sta		|time_cnt
				bcs		@fix
				cmp		#max_timeout
				bge		@fix
				plp

@shift			beq		@save
				asl		a
				bcc		@mult_loop
				bra		@do_max_time

@time_mult		dc.w	null

@fix			plp

@do_max_time	lda		#max_timeout
@save			ldy		#dib.time_out
				sta		[dib_ptr],y
												;
												; Setup the pointer to our Command
												; list within the DIB on GS/OS Direct
												; Page
												;
@over			ldx		|gsos_dpage
				clc
				lda		<dib_ptr

				adc		#dib.scsi_slot

				sta		>smgr_pl_ptr,x
				lda		<dib_ptr+2

				adc		#^dib.scsi_slot

				sta		>smgr_pl_ptr+2,x
												;
												; Call the SCSI Manager via the
												; S_DISPATCHER
												;
				lda		|scsi_mgrnum			;Get the Managers ID
				ldx		|manager_cmd			;Get Manager Command Number
				jmp		rout2_s_disp

				EJECT
												;
												; Bitmaps used to check
												; for command errors.
												;
												; Offset into DIB to where
												; the WORD is that holds the
												; bit for this group.
												;
bm_cmd_offset	dc.w	dib.group0				;group0	Low  WORD
				dc.w	dib.group0+2			;		High WORD
				dc.w	dib.group1				;group1	Low  WORD
				dc.w	dib.group1+2			;		High WORD
				dc.w	dib.group2				;group2	Low  WORD
				dc.w	dib.group2+2			;		High WORD
				dc.w	dib.group3				;group3	Low  WORD
				dc.w	dib.group3+2			;		High WORD
				dc.w	dib.group4				;group4	Low  WORD
				dc.w	dib.group4+2			;		High WORD
				dc.w	dib.group5				;group5	Low  WORD
				dc.w	dib.group5+2			;		High WORD
				dc.w	dib.group6				;group6	Low  WORD
				dc.w	dib.group6+2			;		High WORD
				dc.w	dib.group7				;group7	Low  WORD
				dc.w	dib.group7+2			;		High WORD
												;
												; Mask for commands $00 - $0F
												; in the low word and commands
												; $10 - $1F in the high word.
												;
bm_cmd_mask		dc.w	bit_7					;CMD $x0 Bit 7 Low Byte
				dc.w	bit_6					;CMD $x1 Bit 6 Low Byte
				dc.w	bit_5					;CMD $x2 Bit 5 Low Byte
				dc.w	bit_4					;CMD $x3 Bit 4 Low Byte
				dc.w	bit_3					;CMD $x4 Bit 3 Low Byte
				dc.w	bit_2					;CMD $x5 Bit 2 Low Byte
				dc.w	bit_1					;CMD $x6 Bit 1 Low Byte
				dc.w	bit_0					;CMD $x7 Bit 0 Low Byte
				dc.w	bit_15					;CMD $x8 Bit 7 High Byte
				dc.w	bit_14					;CMD $x9 Bit 6 High Byte
				dc.w	bit_13					;CMD $xA Bit 5 High Byte
				dc.w	bit_12					;CMD $xB Bit 4 High Byte
				dc.w	bit_11					;CMD $xC Bit 3 High Byte
				dc.w	bit_10					;CMD $xD Bit 2 High Byte
				dc.w	bit_9					;CMD $xE Bit 1 High Byte
				dc.w	bit_8					;CMD $xF Bit 0 High Byte

				ENDP

				EJECT

				END
