********************************************************************
* SCSISYS - CoCo SCSI Device Driver
*
* $Id: scsisys.asm,v 1.1.1.1 2001/02/21 23:30:52 boisy Exp $
*
* SCSISYS was developed by Matt Thompson.  However, trying to locate
* this elusive fellow has been, to say the least, frustrating.
* Therefore, for the good of the community, I've chosen to disassemble
* and improve on Matt's work, since SCSISYS is (in my opinion) the
* BEST SCSI driver available for the CoCo.
*
* All pompous idealists with copyright infringement lectures can
* redirect their arguments to /nil
*
* SCSI NOTES
* ==========
* An invaluable resource used to restore comments and understanding
* to this code was the document "Disc Drive SCSI 2/SCSI 3 Interface",
* found at Seagate's web site:  http://www.seagate.com
*
* Ed.    Comments                                       Who YY/MM/DD
* ------------------------------------------------------------------
*  0     Original SCSISYS 2.2 distribution version
* 22     Updated to reflect anticipated 2.3 release     BGP 99/05/30

         nam   SCSISYS
         ttl   CoCo SCSI Device Driver

         ifp1  
         use   defsfile
         endc  

tylg     set   Drivr+Objct
atrv     set   ReEnt+rev
rev      set   $01
edition  set   23

*
* Configurable definitions
*
RetryCnt equ   5          number of times to retry command
MMUSlot  equ   6          8K block area for buffer (0-6)
DrvCount equ   8          maximum number of drives in drive table


****************** DO NOT CHANGE ANYTHING BELOW!!! *******************

SS.SCSI  equ   $DE
MMUBlk   equ   DAT.Regs+MMUSlot 8K block area to use for buffering

*
* SCSI Packet Command Bytes
*
S$REZERO equ   $01
S$REQSEN equ   $03
S$FORMAT equ   $04
S$MODSEL equ   $15
S$READEX equ   $28
S$WRITEX equ   $2A

*
* SCSISYS Defs
*
* IT.DNS bits
DNS.512  equ   $80
DNS.ERR  equ   $10
DNS.CACH equ   $08
DNS.TURB equ   $04
*
* IT.TYP bits
TYP.FUDG equ   $40
TYP.6309 equ   $04

* Ken-Ton Controller Definitions
         ifne  Kenton
SCSIDATA equ   0
SCSISTAT equ   1
SCSISEL  equ   2
SCSIRST  equ   3

REQ      equ   $01
BUSY     equ   $02
MSG      equ   $04
CMD      equ   $08
INOUT    equ   $10
ACK      equ   $20
SEL      equ   $40
RST      equ   $80
         endc  

* Cloud-9 TC^3 Controller Definitions
         ifne  TC3
SCSIDATA equ   0
SCSISTAT equ   1
SCSISEL  equ   1
SCSIRST  equ   1          INVALID, but not used

REQ      equ   $01
BUSY     equ   $02
MSG      equ   $04
CMD      equ   $08
INOUT    equ   $10
         endc  

* Disto SCSI Controller Definitions
         ifne  Disto
SCSIDATA equ   0
SCSISTAT equ   -2
SCSISEL  equ   -1
SCSIRST  equ   -2

SEL      equ   $00
BUSY     equ   $01
ACK      equ   $02
MSG      equ   $04
INOUT    equ   $20
CMD      equ   $40
REQ      equ   $80
         endc  

         mod   eom,name,tylg,atrv,start,size

         org   0
StatPtr  rmb   2
DriveNum rmb   1
SectHi   rmb   1
SectLo   rmb   2
PdBuf    rmb   2
DNS      rmb   1
PathDesc rmb   2
STEP     rmb   1
u000C    rmb   1
u000D    rmb   2
u000F    rmb   1
SCSIPkt  rmb   2          address of SCSI packet passed via SS.SCSI
DoTurbo  rmb   1
         ifne  MultiPak
OrgMPIVl rmb   1
NewMPIVl rmb   1
         endc
DoCache  rmb   1
SectCntr rmb   1          used to loop through 256 byte cache entries
CacheBlk rmb   1

***** START of error packet returned from REQUEST SENSE command
ReqSnPkt equ   .
R$Error  rmb   1          SCSI error code return value
         rmb   1
R$Error2 rmb   1
         rmb   3
* For some reason, these flags are in the middle of the error
* packet!  Don't know why +BGP+
CchInit  rmb   1          cache init flag
         rmb   2
DNMatch  rmb   2
LoMatch  rmb   1
R$AddSns rmb   1
ReqPkL   equ   .-ReqSnPkt length of packet
***** END of error packet returned from REQUEST SENSE command

         rmb   16
CacheSpt rmb   2
CachAddr rmb   2
LoSect   rmb   2
Coherent rmb   1          cache coherency flag (0 = coherent, 1 = not)
evenodd  rmb   1
u003B    rmb   1
u003C    rmb   1
u003D    rmb   1
u003E    rmb   1
u003F    rmb   2
SCSIID   rmb   1
Retries  rmb   1
* SCSI Command Packet
SCSICMD  rmb   1
SCSILUN  rmb   1
SCSIPrm0 rmb   1
SCSIPrm1 rmb   1
SCSIPrm2 rmb   1
SCSIPrm3 rmb   1
SCSIPrm4 rmb   1
SCSIPrm5 rmb   1
SCSIPrm6 rmb   1
SCSIPrm7 rmb   4
XtraBuf  rmb   256
         rmb   DRVBEG+(DRVMEM*DrvCount)
size     equ   .

         fcb   $FF

name     fcs   /SCSISYS/
         fcb   edition

start    lbra  Init
         bra   Read
         nop   
         lbra  Write
         lbra  GetStat
         lbra  SetStat
Term     clrb  
         rts   

* Read
*
* Entry:
*    B  = MSB of the disk's LSN
*    X  = LSB of the disk's LSN
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
Read     lbsr  ChangeU    point U to driver statics
         cmpx  #$0000     LSN 0?
         bne   ReadSect   branch if not
         tstb             LSN 0?
         bne   ReadSect   branch if not
         bsr   ReadSect   else read LSN0
         bcc   LSN0       do LSN0 processing
         rts              return to caller

* Code to deal with copying LSN0
LSN0     ldx   StatPtr,u  get pointer to driver static
         leax  DRVBEG,x   point X to start of drive table
         ldy   PathDesc,u get formerly saved path desc ptr
         ldb   <PD.DRV,y  get drive number
L0041    beq   CopyLSN0   branch if zero
         leax  <DRVMEM,x  else increase X by drive table size
         decb             decrement drive number
         bra   L0041      branch to loop
* X = drive table pointer for PD.DRV
* Copy DD.SIZ bytes (LSN0) from buffer to drive table
CopyLSN0 ldb   #DD.SIZ
         ldu   PdBuf,u
CpyLSNLp lda   ,u+
         sta   ,x+
         decb  
         bne   CpyLSNLp
         rts   

*
* Read Sector
*
* Passed:  B = Sector bits 23-16
*          X = Sector bits 15-0
*
ReadSect lbsr  SetUpPkt
         tst   <DoCache,u caching on?
         beq   L00A5      branch if not...
         lbsr  SetCache   set up cache
         tst   <Coherent,u is cache coherent?
         beq   FrmCache   yes, read from cache
         bsr   FrmCntrl   else read from SCSI device
         bcc   FrmCache   and copy from cache
         rts   

* Copy from cache
FrmCache ldx   <CacheSpt,u source
         pshs  y          save Y
         ldy   PdBuf,u    destination
         lbsr  Copy256    copy from X to Y
         puls  pc,y       restore Y and return

* Read from SCSI device
FrmCntrl ldb   #S$READEX
         lbsr  MkPktM
FrmCntr2 lbsr  SndSCMD    send SCSI command
         bcc   FilCache   fill the cache
         lbsr  ResetAll
         rts   

FilCache ldb   #2048/256     number of 256 blocks in one cache segment
         stb   <SectCntr,u   save off in segment counter
         ldx   <CachAddr,u
L008D    bita  #CMD          command phase?
         bne   L009E         yes
         lbsr  ReadData      else data phase, read data
         dec   <SectCntr,u   decrement sector counter
         beq   L009E
         lbsr  Wait4REQ
         bra   L008D
L009E    leax  >FrmCntr2,pcr
         lbra  CheckTfr

L00A5    tst   <u003B,u
         beq   L00D7
         tst   DNS,u      512 byte flag set?
         bpl   L00D7      branch if not...
         tst   <evenodd,u is sector odd or even?
         beq   L00D7      branch if even
         ldb   DriveNum,u
         cmpb  <u003D,u
         bne   L00D4
         ldd   SectLo,u
         cmpd  <u003F,u
         bne   L00D4
         ldb   SectHi,u
         cmpb  <u003E,u
         bne   L00D4
         leax  <XtraBuf,u source
         ldy   PdBuf,u    destination
         lbsr  Copy256
         clrb  
         rts   
L00D4    clr   <u003B,u
L00D7    ldb   #S$READEX
         lbsr  MkPktS
L00DC    lbsr  SndSCMD
         bcc   L00E2
         rts   
L00E2    bita  #CMD       command phase
         bne   L0115      yes
         ldx   PdBuf,u    else load pdesc buffer in X
         tst   DNS,u      512 byte flag set?
         bpl   L0113      branch if not
         tst   <evenodd,u is sector odd or even?
         bne   L0110      branch if odd
         bsr   ReadData
         leax  <XtraBuf,u
         bsr   ReadData
         lda   #$01
         sta   <u003B,u
         lda   SectHi,u
         sta   <u003E,u
         ldd   SectLo,u
         orb   #$01
         std   <u003F,u
         lda   DriveNum,u
         sta   <u003D,u
         bra   L0115
L0110    lbsr  Waste256
L0113    bsr   ReadData
L0115    leax  >L00DC,pcr
         lbra  CheckTfr

*
* Read Data from controller
*
* Passed:  X = Address of buffer
*          Y = Controller Address
*
ReadData tst   <DoTurbo,u use turbo mode?
         bne   RegRead    branch if not...

         ifne  H6309

* 6309 TURBO READ
         bsr   CacheIn
         pshsw  
         ldw   #256
         tfm   y,x+
         pulsw  
         bsr   CacheOut
         rts   

         else  

* 6809 TURBO READ
         lda   #16
         pshs  a
L0125    lbsr  CacheIn
L0128    lda   SCSIDATA,y
         ldb   SCSIDATA,y
         std   ,x
         lda   SCSIDATA,y
         ldb   SCSIDATA,y
         std   $02,x
         lda   SCSIDATA,y
         ldb   SCSIDATA,y
         std   $04,x
         lda   SCSIDATA,y
         ldb   SCSIDATA,y
         std   $06,x
         lda   SCSIDATA,y
         ldb   SCSIDATA,y
         std   $08,x
         lda   SCSIDATA,y
         ldb   SCSIDATA,y
         std   $0A,x
         lda   SCSIDATA,y
         ldb   SCSIDATA,y
         std   $0C,x
         lda   SCSIDATA,y
         ldb   SCSIDATA,y
         std   $0E,x
         leax  <16,x
         dec   ,s     decrement counter
         lda   ,s     get counter
         bita  #$07   
         bne   L0128
         lbsr  CacheOut
         tsta  
         bne   L0125
         puls  pc,a

         endc  

*
* "Non-Turbo" Read Data from controller
*
* Passed:  X = Address of buffer
*          Y = Controller Address
*
RegRead  clrb  
RegRead2 bsr   CacheIn
RegRead3 lda   SCSISTAT,y
         bita  #REQ
         beq   RegRead3
         lda   SCSIDATA,y
         sta   ,x+
         decb  
         bitb  #$0F
         bne   RegRead3
         bsr   CacheOut
         tstb  
         bne   RegRead2
         rts   

CacheIn  
         ifne  MultiPak
         bsr   MapInDev
         endc
         bsr   MapIn8K
         rts   

CacheOut
         bsr   MapOut8K
         ifne  MultiPak
         bsr   MapOutDv
         endc
         andcc #^IntMasks
         rts   

* Multi-Pak selection code
         ifne  MultiPak
MapInDev pshs  a
         orcc  #IntMasks
         lda   $FF7F
         sta   <OrgMPIVl,u
         anda  #%11110000
         ora   <NewMPIVl,u
         sta   $FF7F
         puls  pc,a

MapOutDv pshs  a
         lda   <OrgMPIVl,u
         sta   $FF7F
         puls  pc,a

GetSCST  bsr   MapInDev
         lda   SCSISTAT,y
         bsr   MapOutDv
         andcc #^IntMasks
         rts

GetSCDT  bsr   MapInDev
         lda   SCSIDATA,y
         bsr   MapOutDv
         andcc #^IntMasks
         rts

PutSCDT  bsr   MapInDev
         sta   SCSIDATA,y
         bsr   MapOutDv
         andcc #^IntMasks
         rts
         endc

* Map in our 8K buffer block
* NOTE: This routine masks interrupts!
MapIn8K  pshs  a
         orcc  #IntMasks
         lda   <CacheBlk,u
         sta   >MMUBlk
         puls  pc,a

* Map out our 8K buffer block
MapOut8K pshs  x,a
         ldx   <D.SysDAT
         lda   (MMUSlot*2)+1,x
         sta   >MMUBlk
         puls  pc,x,a

* Throw 256 bytes from the controller out to the bit bucket
Waste256 tst   <DoTurbo,u use turbo mode?
         bne   RegWaste      branch if not...

         ifne  MultiPak
         bsr   MapInDev
         endc

* TURBO WASTE
         ldb   #32
L01A9    lda   SCSIDATA,y
         lda   SCSIDATA,y
         lda   SCSIDATA,y
         lda   SCSIDATA,y
         lda   SCSIDATA,y
         lda   SCSIDATA,y
         lda   SCSIDATA,y
         lda   SCSIDATA,y
         decb  
         bne   L01A9
         ifne  MultiPak
         bsr   MapOutDv
         andcc #^IntMasks
         endc
         rts   

RegWaste clrb  
         ifne  MultiPak
RegWast1 bsr   MapInDev
         endc
RegWast2
         lda   SCSISTAT,y
         bita  #REQ
         beq   RegWast2
         lda   SCSIDATA,y
         decb  
         ifne  MultiPak
         bitb  #$0F
         endc
         bne   RegWast2
         ifne  MultiPak
         bsr   MapOutDv
         andcc #$AF
         tstb
         bne   RegWast1
         endc
         rts   

*
* Check Transfer Status
*
* Passed:  X = address of routine to call
*
CheckTfr lbsr  GetStatB   get transfer status byte
         tst   <DoCache,u caching on?
         bne   L01DB      branch if so...
         ldb   <u003B,u
         stb   <u003C,u
         clr   <u003B,u

L01DB
         ifne  Disto
         bita  #$08
         else
         bita  #CMD       CMD bit set?
         endc

         beq   L01E1      branch if not...
L01DF    jmp   ,x

L01E1
         ifne  Disto
         bita  #ACK
         else
         bita  #BUSY      BUSY set?
         endc

         beq   L01ED      branch if not...
* Error occurred.. retry
         dec   <Retries,u decrement retry count
         bne   L01DF      try again if not at end
         lbra  SCSIErr    else get error
L01ED    tst   <DoCache,u caching on?
         bne   L01F8      branch if so...
         ldb   <u003C,u
         stb   <u003B,u
L01F8    clrb  
         rts   

* Write
*
* Entry:
*    B  = MSB of the disk's LSN
*    X  = LSB of the disk's LSN
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
Write    lbsr  ChangeU    point U to driver statics
         lbsr  ResetAll
         lbsr  SetUpPkt
         tst   DNS,u      512 byte flag set?
         bpl   Write1S    branch if not...
         clr   <u003B,u
         bsr   L020F
         bcc   L023C
         rts   

* Send Read command for a single logical sector
L020F    ldb   #S$READEX
         lbsr  MkPktS
L0214    lbsr  SndSCMD
         bcc   L021A
         rts   

L021A    bita  #CMD
         bne   L0236
         leax  <XtraBuf,u
         tst   <evenodd,u is sector odd or even?
         bne   L022B      branch if odd
* even sector read
         lbsr  Waste256   waste first 256 bytes
         bra   L0233      then read other 256 bytes
L022B    lbsr  ReadData   read first 256 bytes
         lbsr  Waste256   then waste other 256 bytes
         bra   L0236
L0233    lbsr  ReadData
L0236    leax  >L0214,pcr
         bra   CheckTfr
L023C    ldb   #RetryCnt
         stb   <Retries,u
Write1S  ldb   >WriteCmd,pcr
         lbsr  MkPktS
Write1S2 lbsr  SndSCMD
         bcc   L024E
         rts   
L024E    bita  #CMD
         bne   L026D
         tst   DNS,u      check for 512 flag
         bpl   L0269      branch if not set
         tst   <evenodd,u is sector odd or even?
         bne   L0264      branch if odd
         ldx   PdBuf,u    load X with path desc buffer
         bsr   WritData   write first 256 bytes
         leax  <XtraBuf,u point to other half of 512 byte buffer
         bra   L026B
L0264    leax  <XtraBuf,u point to 2nd half of 512 byte buffer
         bsr   WritData   write data
L0269    ldx   PdBuf,u    load PD buffer
L026B    bsr   WritData   write data
L026D    leax  >Write1S2,pcr
         lbra  CheckTfr

*
* Write Data to controller
*
* Passed:  X = Address of data to write
*          Y = Controller Address
*
WritData tst   <DoTurbo,u use turbo mode?
         bne   RegWrite   branch if not

         ifne  H6309

* 6309 TURBO WRITE
         lbsr  CacheIn
         pshsw  
         ldw   #256
         tfm   x+,y
         pulsw  
         lbsr  CacheOut
         rts   

         else  

* 6809 TURBO WRITE
         lda   #16
         pshs  a
L027D    lbsr  CacheIn
L0280    ldd   ,x
         sta   SCSIDATA,y
         stb   SCSIDATA,y
         ldd   $02,x
         sta   SCSIDATA,y
         stb   SCSIDATA,y
         ldd   $04,x
         sta   SCSIDATA,y
         stb   SCSIDATA,y
         ldd   $06,x
         sta   SCSIDATA,y
         stb   SCSIDATA,y
         ldd   $08,x
         sta   SCSIDATA,y
         stb   SCSIDATA,y
         ldd   $0A,x
         sta   SCSIDATA,y
         stb   SCSIDATA,y
         ldd   $0C,x
         sta   SCSIDATA,y
         stb   SCSIDATA,y
         ldd   $0E,x
         sta   SCSIDATA,y
         stb   SCSIDATA,y
         leax  16,x
         dec   ,s
         lda   ,s
         bita  #$07
         bne   L0280
         lbsr  CacheOut
         tsta  
         bne   L027D
         puls  pc,a

         endc  

*
* "Non-Turbo" Write Data to controller
*
* Passed:  X = Address of data to write
*          Y = Controller Address
*
RegWrite clrb  
RegWrit2 lbsr  CacheIn
RegWrit3 lda   SCSISTAT,y
         bita  #REQ
         beq   RegWrit3
         lda   ,x+
         sta   SCSIDATA,y
         decb  
         bitb  #$0F
         bne   RegWrit3
         lbsr  CacheOut
         tstb  
         bne   RegWrit2
         rts   

*
* Send a REQSENSE message to the SCSI controller
*
SndMSG   lbsr  Wait4REQ   wait for REQ to be asserted
         bita  #CMD       command phase?
         beq   INorOUT    no, check for in/out
         rts   
INorOUT  bita  #INOUT     data coming in or going out?
         bne   ComingIn   branch if coming in...
         lda   ,x+
         ifne  MultiPak
         lbsr  PutSCDT
         else
         sta   SCSIDATA,y
         endc
         bra   SndMSG
ComingIn 
         ifne  MultiPak
         lbsr  GetSCDT
         else
         lda   SCSIDATA,y
         endc
         sta   ,x+
         bra   SndMSG

* Get Status Byte from SCSI controller
GetStatB lbsr  Wait4REQ
         ifne  MultiPak
         lbsr  GetSCDT
         else
         lda   SCSIDATA,y
         endc
         pshs  a
         lbsr  Wait4REQ
         clra  
         ifne  MultiPak
         lbsr  PutSCDT
         else
         sta   SCSIDATA,y
         endc
         puls  pc,a

*
* Set up for SCSI Packet
*
* Passed:   B = Sector Hi byte
*           X = Sector Mid/Low bytes
*
* Returns:  Y = Device Port Address
*
SetUpPkt stb   SectHi,u   save sector hi byte
         stx   SectLo,u   save sector lo bytes
         ldb   <PD.DNS,y  get density byte in path desc.
         stb   DNS,u      save in our statics
         ldd   SectLo,u   get lo sector
         andb  #%11111000 eliminate page bits
         std   <LoSect,u  save off
         clra  
         tst   >CacheFlg,pcr
         beq   L0322
         ldb   DNS,u      get DNS byte
         bitb  #DNS.CACH  cache flag set?
         bne   L0322      yes, branch
         inca  
L0322    sta   <DoCache,u save cache flag
         ldb   DNS,u      get DNS byte
         andb  #DNS.TURB
         stb   <DoTurbo,u save turbo flag
         ldb   DNS,u      get DNS byte
         andb  #$03       preserve SCSI ID's
         leax  >SIDTBL,pcr point to SCSI ID table
         lda   b,x        get SCSI ID mask
         sta   <SCSIID,u  save off
         ldd   PD.BUF,y   get sector buffer address
         std   PdBuf,u    save off in our statics
         lda   #RetryCnt  get retry count
         sta   <Retries,u save off
         lda   <PD.DRV,y  get drive number from path desc.
         sta   DriveNum,u save off in our statics
         ifne  MultiPak
         lda   <PD.STP,y
         anda  #$03
         sta   <NewMPIVl,u
         endc
         lda   <PD.STP,y  get step rate byte from path desc.
         anda  #$FC       preserve all but lower 2 bits
         sta   STEP,u     save off in our statics
         tst   DNS,u      512 byte flag on?
         bpl   L0359      branch if not...
         lda   SectLo+1,u get lo byte of sector
         anda  #$01       if result == 0, then even, else odd sector
         sta   <evenodd,u save off even/odd flag
L0359    sty   PathDesc,u save off path descriptor
         ldy   StatPtr,u  load Y with pointer to driver static
         ldy   V.PORT,y   stuff port address in Y
         rts   


* SCSI ID Table
SIDTBL   fcb   $01,$02,$04,$08

* Make SCSI Packet (Multiple block read)
* B = SCSI Command
MkPktM   lda   #$04       4 logical blocks
         tst   DNS,u      512 byte flag on?
         bmi   MkPkt      branch if so...
         lsla             8 logical blocks
         bra   MkPkt 

* Make SCSI Packet (single block read)
* B = SCSI Command
MkPktS   lda   #$01       one logical block
MkPkt    sta   <SCSIPrm6,u
         stb   <SCSICMD,u put passed SCSI command
         clra  
         clrb  
         std   <SCSILUN,u clear SCSI LUN
         std   <SCSIPrm4,u clear RESERVED and transfer MSB
         sta   <SCSIPrm7,u clear flag byte
         ldb   SectHi,u   get sector MSB
         tst   DNS,u      512 byte flag set?
         bpl   MkPkt2     branch if not...
         lsrb             else divide SectHi by 2
MkPkt2   stb   <SCSIPrm1,u
         pshs  cc
         lda   <SCSIPrm6,u
         cmpa  #$01       one sector transfer length?
         beq   L039B
         ldd   <LoSect,u
         bra   L039D
L039B    ldd   SectLo,u   get lower 2 bytes of sector into D
L039D    puls  cc
         tst   DNS,u      512 byte flag set?
         bpl   L03A5      branch if not...
         rora             divide D by 2
         rorb  
L03A5    std   <SCSIPrm2,u
         rts   

*
* SCSI Packet Send Routine
* 
* Sets LUN for SCSI Packet, then sends command packet to controller
*
* Passed:  Y = Device Address
*
* Returns: A = SCSI Status byte
*
SndSCMD  ldb   <SCSILUN,u get SCSI LUN
         andb  #%00011111 Mask out LUN
         stb   <SCSILUN,u
         ldb   DNS,u      get DNS byte
         andb  #%01100000 get SCSI LUN from byte
         orb   <SCSILUN,u OR with SCSI LUN byte
         stb   <SCSILUN,u save off
SndSCMD2 
         ifne  MultiPak
         lbsr  GetSCST
         else
         lda   SCSISTAT,y get SCSI status byte
         endc
         bita  #BUSY      BUSY?
         bne   SndSCMD2   branch if so...
* BUSY is clear
         ifne  MultiPak
         lbsr  MapInDev
         endc
         lda   <SCSIID,u  get target's SCSI ID byte
         sta   SCSIDATA,y write out to controller
         sta   SCSISEL,y  here too...
SndSCMD3 
         ifne  MultiPak
         lbsr  GetSCST
         else
         lda   SCSISTAT,y get SCSI status byte
         endc
         bita  #BUSY      BUSY?
         beq   SndSCMD3   branch if so...
* BUSY is set
         leax  <SCSICMD,u point X to SCSI command packet
SndSCMD4 bsr   Wait4REQ   wait for REQ
         bita  #CMD       SCSI CMD bit set?
         beq   SndSMCD5   branch if not...
         bita  #INOUT     INOUT set?
         bne   SndSMCD5   branch if so...
         lda   ,x+        get byte from SCSI CMD packet
         ifne  MultiPak
         lbsr  PutSCDT
         else
         sta   SCSIDATA,y write to controller
         endc
         bra   SndSCMD4
SndSMCD5 clrb  
         rts   

*
* Loop until REQ bit is set
*
* Checks are made for system process, which shouldn't sleep
*
* Passed:   Y = Device Address
*
* Returns:  A = SCSI Status byte
*
Wait4REQ pshs  x
Wait4RQ2 
         ifne  MultiPak
         lbsr  GetSCST
         else
         lda   SCSISTAT,y get SCSI status byte
         endc
         bita  #REQ       REQ set?
         beq   Wait4RQ3   branch if not
         puls  pc,x       else return
* Give up timeslice several times unless this is the system
Wait4RQ3 ldx   <D.Proc    get proc descriptor
         cmpx  <D.SysPrc  system?
         beq   Wait4RQ2   yep, continue checking...
         ldx   <D.AProcQ  else get active proc queue
         beq   Wait4RQ2   if empty, continue checking
         ldx   #$0001
         os9   F$Sleep    give up timeslice
         ldx   <D.AProcQ  get active proc queue
         beq   Wait4RQ2   if empty, continue checking
         ldx   #$0001
         os9   F$Sleep    give up timeslice
         bra   Wait4RQ2   continue checking


* Point U to end of drive table
ChangeU  pshs  b,a
         lda   #DRVMEM    drive table size    ($26)
         ldb   >NumDrvs,pcr no. of drive tables ($08)
         mul              end of drive tables ($130)
         addd  #DRVBEG    add start of tables (+$0F = $13F)
         stu   d,u        save U at U+$13F
         leau  d,u        point U to U+$13F
         puls  pc,b,a

* Init
*
* Entry:
*    Y  = address of device descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
Init     ldb   >NumDrvs,pcr
         stb   V.NDRV,u
         leax  DRVBEG,u
         lda   #$FF
* Invalidate V.NDRV drive tables
L0425    sta   DD.TOT,x
         sta   DD.TOT+1,x
         sta   DD.TOT+2,x
         leax  <DRVMEM,x
         decb  
         bne   L0425
         ldy   V.PORT,u
         bsr   ChangeU    point U to driver statics
         tst   >ResetFlg,pcr
         beq   L044D
         ifne  MultiPak
         ldb   <IT.STP,y
         andb  #$03
         stb   <NewMPIVl,u
         lbsr  MapInDev
         endc
         clra  
         sta   SCSIDATA,y
         clrb  
L0440
         ifne  Disto
         sta   SCSIRST,y
         else
         lda   SCSIRST,y
         endc

         decb  
         bne   L0440
* Delay of some sort
         ifne  MultiPak
         lbsr  MapOutDv
         endc
         ldx   #$0000
L0448    mul   
         leax  -1,x
         bne   L0448
* Check for flag to see if we go through and find an 8K block
L044D    lda   >CacheFlg,pcr
         beq   L046A
* Scan memory block list, looping until free block is found
         orcc  #IntMasks
         ldx   #$0200     start at beginning of block
* Note: I see a potential problem here.  What if all blocks in the
*       block map are allocated?  This loop could go PAST the
*       block map! BGP 5/30/99
L0458    lda   ,x+        get flag for this entry
         bne   L0458      if not free, continue searching
         tfr   x,d        D holds entry of free 8K block + 1
         decb             decrement B to get proper block entry
         stb   <CacheBlk,u save it
         lda   #RAMInUse
         sta   -1,x       mark block as "RAM In Use"
         andcc  #^IntMasks
         bsr   ResetAll
L046A    clr   <u003B,u
         clrb  
         rts   

ResetAll pshs  b,a,cc
         clra  
         clrb  
         sta   <ReqSnPkt,u    clear error code
         sta   <ReqSnPkt+2,u
         sta   <ReqSnPkt+4,u
         std   <CchInit,u    clear cache init flag
         sta   <ReqSnPkt+8,u
         std   <ReqSnPkt+21,u
         std   <ReqSnPkt+23,u
         std   <ReqSnPkt+25,u
         std   <CacheSpt,u   clear cache spot
         puls  pc,b,a,cc

* SetStat
*
* Entry:
*    A  = function code
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
SetStat  lbsr  ChangeU    point U to driver statics
         bsr   ResetAll
         cmpa  #SS.SQD
         beq   ParkHD
         cmpa  #SS.Reset
         beq   SetRet
         cmpa  #SS.WTrk
         beq   SetRet
         cmpa  #SS.SCSI
         bne   GetStat
         lda   <PD.TYP,y  get type byte
         ldb   #$04       R$X offset for 6809 is at 4
         bita  #TYP.6309  is 6309 flag set?
         beq   L04B0      branch if not...
         addb  #$02       else R$X is actually at 6
L04B0    stb   u000F,u    save off offset
         ldx   PD.RGS,y   X = caller regs
         ldx   b,x        get R$X (SCSI command/data packet)
         stx   <SCSIPkt,u save pointer to SCSI packet
         ldb   PD.CPR,y   get caller's PID
         ldx   <D.PrcDBT  get ptr to proc table
         lda   b,x        get ptr to proc's process descriptor
         ldb   #P$Task    get task offset into B
         tfr   d,x
         ldb   ,x         B now holds task #
         stb   u000C,u    save off
         ldd   PD.RGS,y   get caller regs pointer
         std   u000D,u    save off

* GetStat
*
* Entry:
*    A  = function code
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
GetStat  ldb   #E$UnkSvc
         coma  
         rts   

ParkHD   lbsr  SetUpPkt
ParkHD2  lda   #S$MODSEL
         sta   <SCSICMD,u
         lda   #$01
         sta   <SCSILUN,u
         clra  
         clrb  
         std   <SCSIPrm0,u
         std   <SCSIPrm2,u
         lbsr  SndSCMD   send SCSI command to controller
         bcs   ParkRet
         leax  >ParkHD2,pcr
         lbra  CheckTfr
SetRet   clrb  
ParkRet  rts   

         ifne  H6309

* 6309 COPY
* Copy 256 bytes
*
* Passed:  X = source
*          Y = destination
*
Copy256  lbsr  MapIn8K
         pshsw  
         ldw   #256
         tfm   x+,y+
         pulsw  
         lbsr  MapOut8K
         andcc  #^IntMasks
         rts   

         else  

* 6809 COPY
* Copy 256 bytes
*
* Passed:  X = source
*          Y = destination
*
Copy256  pshs  u
         ldb   #$10
L04F6    lbsr  MapIn8K
L04F9    ldu   ,x
         stu   ,y
         ldu   $02,x
         stu   $02,y
         ldu   $04,x
         stu   $04,y
         ldu   $06,x
         stu   $06,y
         ldu   $08,x
         stu   $08,y
         ldu   $0A,x
         stu   $0A,y
         ldu   $0C,x
         stu   $0C,y
         ldu   $0E,x
         stu   $0E,y
         leax  $10,x
         leay  $10,y
         decb  
         bitb  #$03
         bne   L04F9
         lbsr  MapOut8K
         andcc  #^IntMasks
         tstb  
         bne   L04F6
         puls  pc,u

         endc  

*
* SetCache
*
* Determines whether cache is coherent
*
SetCache clr   <Coherent,u    assume cache is coherent
         tst   <CchInit,u     cache initialized yet?
         beq   InitCach       if not, do it!
         ldd   DriveNum,u     get drive num/sector hi byte
         cmpd  <DNMatch,u     same as before
         bne   InitCach       nope, need to update
         ldx   <LoSect,u      get LoSect
         cmpx  <LoMatch,u     same as LoMatch?
         beq   SetCchRd       branch if so...
* Initialize the cache
InitCach lda   #$01           load A with TRUE
         sta   <Coherent,u    make cache non-coherent
         sta   <CchInit,u     set cache init flag
         ldd   DriveNum,u     get drive num and sector hi byte
         std   <DNMatch,u     save in flag area
         ldd   <LoSect,u      get lo sector
         std   <LoMatch,u     save in flag area
* Set cache read spot
SetCchRd ldd   #MMUSlot*$2000 get address of 8K cache block
         std   <CachAddr,u    save off
         lda   SectLo+1,u     get lo byte of sector
         anda  #%00000111     A = page
         addd  #MMUSlot*$2000 add page to 8k cache base addr
         std   <CacheSpt,u    save off
         rts   

* An error occurred - do a REQUEST SENSE to find the error
SCSIErr  lda   #S$REQSEN  REQUEST SENSE command
         sta   <SCSICMD,u
         clra  
         clrb  
         sta   <SCSILUN,u
         std   <SCSIPrm0,u
         stb   <SCSIPrm3,u
         lda   #ReqPkL     set allocation length
         sta   <SCSIPrm2,u
         lbsr  SndSCMD     send command
         bcs   ErrErr      branch if error
         leax  <R$Error,u  point to return data buffer
         lbsr  SndMSG      get response data
         lbsr  GetStatB
         clra  
         pshs  a
         lda   R$Error,u  get error code
         anda  #$7F       wipe out hi bit
         cmpa  #$70       "current" error?
         bne   L05A3      branch if not...
         lda   R$Error2,u get more detailed error
         anda  #%00001111
         sta   ,s         save off stack
         lda   R$AddSns,u get additional sense code
L05A3    ldb   DNS,u      get DNS byte
L05A5    bitb  #DNS.ERR   send OS-9 Error?
L05A7    beq   L05AD      branch if so...
         tfr   a,b        else put SCSI error in B
         bra   ErrErr     and return with error
* Walk error table to find OS-9 error
L05AD    leax  >ErrTbl,pcr
         ldb   #E$Unit
L05B3    tst   ,x
         beq   ErrErr
         cmpa  ,x++
         bne   L05B3
         ldb   -1,x
ErrErr   coma  
         puls  pc,a

* Error Table - Maps SCSI errors to OS-9 errors
ErrTbl   fcb   $01,E$NotRdy
         fcb   $02,E$Seek
         fcb   $03,E$Write
         fcb   $04,E$NotRdy
         fcb   $06,E$Seek
         fcb   $10,E$CRC
         fcb   $11,E$Read
         fcb   $12,E$Read
         fcb   $13,E$Read
         fcb   $14,E$Seek
         fcb   $15,E$Seek
         fcb   $17,E$CRC
         fcb   $18,E$CRC
         fcb   $19,E$IllArg
         fcb   $1A,E$IllArg
         fcb   $1C,E$Read
         fcb   $1E,E$CRC
         fcb   $20,E$IllCmd
         fcb   $21,E$Sect
         fcb   $24,E$Sect
         fcb   $25,E$IllArg
         fcb   $26,E$IllArg
         fcb   $29,E$NotRdy
         fcb   $2A,E$NotRdy
         fcb   $00

*
* SCSISYS Configuration Flags
*
WriteCmd fcb   S$WRITEX   Write command byte

* The RESET code cannot be executed on the Cloud-9 TC^3 since
* there is no SCSI RESET port on that controller
         ifne  TC3
ResetFlg fcb   $00
         else  
ResetFlg fcb   $FF
         endc  

CacheFlg fcb   $FF        Cache flag ($FF = ON, $0 = OFF)
NumDrvs  fcb   DrvCount   Number of supported drives

         emod  
eom      equ   *
         end   
