antoine-source/scsi2/SCSI.Drivers/SCSI Main Driver

1 line
26 KiB
Plaintext
Raw Blame History

;*******************************************************
;
; SCSI Driver 'Main Driver'.
;
; Written by Matt Gulick. Started May 25,1988
;
; Copyright Apple Computer, Inc. 1988,89
;
;*******************************************************
;*******************************************************
;
; 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_cmd2
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_ec
IMPORT lst_rslt_stat
IMPORT lst_rslt_skey
IMPORT lst_rslt_info
IMPORT lst_rslt_rqlen
IMPORT lst_rslt_scode
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 <20> 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 last error stuff
;
stz |lst_rslt_ec
stz |lst_rslt_stat
stz |lst_rslt_skey
stz |lst_rslt_info
stz |lst_rslt_rqlen
stz |lst_rslt_scode
;
; 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 Curren CoMmanD and GRouP
;
lda [scsi_mdrvr]
and #$0fff
sta |cur_cmd2 ;Preserve Current Command Number
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
@exit 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
;
; VALidate the CoMmanD TYPe
;
jsr val_cmd_typ
bcs @exit ;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
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.
;
lda #$ffff
sta |auto_sense_data
sta |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
and |auto_sense_data+2
cmp #$ffff
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.
;
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 ;Check for 8 bits of 16 match
beq @got_r_cmd
cmp |cur_cmd2 ;Check for 12 bits of 16 match
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
; in 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 ;***** Temp Change *****
lda [dib_ptr],y ;***** Temp Change *****
ldy #dib.scsi_slot ;***** Temp Change *****
sta [dib_ptr],y ;***** Temp Change *****
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 <20> 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 <20> Low
-dib.start_blk,y
ldy #dib.start_blk
lda [dib_ptr],y
sta |p_block_num\ ;Stored High <20> 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
;-------------------------------------------------------------------------------
IF scsi_dtype = mcd_40 THEN
;
; It is a Direct Access Magnetic
; Tape Device.
;
lda #$8000
ELSE
;-------------------------------------------------------------------------------
;
; It is not a Direct Access Magnetic
; Tape Device.
;
lda #$0000
ENDIF
;-------------------------------------------------------------------------------
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
beq @do_max_time
@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.scsiid
adc #dib.scsi_slot
sta >smgr_pl_ptr,x
lda <dib_ptr+2
; adc #^dib.scsiid
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