*******************************************************************
* llide - Low-level IDE driver
*
* $Id$
*
* This low level driver works with both ATA and ATAPI devices.
*
* The type of device (ATA or ATAPI) is automatically detected
* by the 'IOSetup' routine.  Additionally, an ATA device is
* further detected as either an LBA or CHS device.
*
* Since only two physical drives are allowed (master/slave),
* there is a two entry "per drive static storage" that indicates
* if a drive has been initialized, its type (ATAPI or ATA, LBA or
* CHS), and if ATA, its geometry.
*
* Edt/Rev  YYYY/MM/DD  Modified by
* Comment
* ------------------------------------------------------------------
*     1    2004/04/08  Boisy G. Pitre
* Created.
*
*     2    2005/07/23  Christopher R. Hawks
* Fixes for persnickity ATAPI CDROMs.
*
*     3    2005/08/21  Christopher R. Hawks
* More fixes.
*
*     4    2005/12/13  Boisy G. Pitre
* Moved SS.VarSect code into RBSuper for performance

               NAM       llide               
               TTL       Low-level IDE driver

               IFP1      
               USE       defsfile
               USE       rbsuper.d
               USE       ide.d
               ENDC      

tylg           SET       Sbrtn+Objct
atrv           SET       ReEnt+rev
rev            SET       4


RW12           SET       0                   Use READ12/WRITE12 ATAPI commands (1 = yes)
WAITTIME       SET       10                  BUSY wait time (in approximate seconds)

*
* Status Register Flip/Mask Values
*
NBUSYDRDY      EQU       (BusyBit|DrdyBit)*256+(DrdyBit)
NBUSY          EQU       (BusyBit)*256+$00
NBUSYDRQ       EQU       (BusyBit|DrqBit)*256+(DrqBit)
NBUSYNDRQ      EQU       (BusyBit|DrqBit)*256+$00

               MOD       eom,name,tylg,atrv,start,0

               IFNE      RW12
READCODE       EQU       A$READ2
WRITCODE       EQU       A$WRITE2
               ELSE      
READCODE       EQU       A$READ
WRITCODE       EQU       A$WRITE
               ENDC      

NumRetries     EQU       8

* Low-level driver static memory area
               ORG       V.LLMem
* Master static storage
V.Master       RMB       1                   status byte (ATAPI or ATA (CHS or LBA))
               RMB       2                   Cylinders (CHS) or Bits 31-16 of LBA
               RMB       1                   Sides (CHS) or Bits 15-8 of LBA
               RMB       2                   Sectors (CHS) or Bits 7-0 of LBA
* Slave drive static storage
V.Slave        RMB       1
               RMB       2
               RMB       1
               RMB       2
* ATAPI Command Packet
V.ATAPICmd     RMB       18
V.SnsData      EQU       V.ATAPICmd          Sense Data is shared with ATAPI command block
* The following values are for device 0 and 1 respectively:
* Bit 0 = device inited (0 = false, 1 = true)
* Bit 1 = device type (0 = ATA, 1 = ATAPI)
* Bit 2 = device mode (0 = CHS, 1 = LBA)
V.CurStat      RMB       1
V.Retries      RMB       1
V.WhichDv      RMB       1                   contains devhead selection (made by IOSetup)
V.PhySct       RMB       3                   local copy of physical sector passed (V.PhySct)
V.SctCnt       RMB       1                   local copy of physical sector passed (V.SectCnt)
V.Sectors      RMB       1                   number of sectors (harvested directly from drive query)
V.CurDTbl      RMB       2
V.ATAVct       RMB       2


name           FCS       /llide/

start          bra       ll_init
               nop       
               lbra      ll_read
               lbra      ll_write
               lbra      ll_getstat
               lbra      ll_setstat

* ll_init - Low level init routine
*
* Entry:
*    Y  = address of device descriptor
*    U  = address of low level device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
* Note: This routine is called ONCE: for the first device
* IT IS NOT CALLED PER DEVICE!
*
ll_init                  
*         clrb
*         rts             


* ll_term - Low level term routine
*
* Entry:
*    Y  = address of device descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
* Note: This routine is called ONCE: for the last device
* IT IS NOT CALLED PER DEVICE!
*
ll_term                  
               clrb      
               rts       


* Entry:   Y = address of per-drive static storage
ATADSize                 
               pshs      y,x,b               make room for space on stack (and save id byte)
* Determine if we are dealing with LBA or CHS
               bitb      #$04                LBA?
               bne       lba@
* Here we pull CHS values
chs@                     
               ldd       1,y                 get cylinders
               std       1,s
               lda       3,y                 get sides
               sta       ,s                  save sides on stack (B)
               ldd       4,y                 get sectors
               std       3,s                 save sectors/track on stack (Y)
               bra       m@
* Here we pull LBA values at words 60-61
lba@                     
               clr       ,s                  clear flag indicating LBA mdoe (B)
               ldd       3,y                 get bits 15-0
               std       3,s                 save bits 15-0 on stack (Y)
               ldd       1,y                 get bits 31-16
               std       1,s                 save bits 31-16 (X)
m@             lda       #$02                512 bytes/sector
ex@            puls      b,x,y,pc


* SSDSize - Get a disk medium's size
*
* GetStat Call SS.DSize:
*
* Entry: B = SS.DSize
* Exit:  Carry = 1; error with code in B
*        Carry = 0:
*          IF B = 0
*            A = Sector Size (1 = 256, 2 = 512, 4 = 1024, 8 = 2048)
*            X = Number of Sectors (bits 31-16)
*            Y = Number of Sectors (Bits 15-0)
*          IF B != 0
*            A = Sector Size (1 = 256, 2 = 512, 4 = 1024, 8 = 2048)
*            X = Number of Logical Cylinders
*            B = Number of Logical Sides
*            Y = Number of Logical Sectors/Track
*  
SSDSize        pshs      u,y
               bsr       DSize
               bcs       ex@
               ldu       ,s                  get path desc in U
               ldu       PD.RGS,u
               std       R$D,u
               stx       R$X,u
               sty       R$Y,u
               clrb      
ex@            puls      y,u,pc

DSize          lbsr      IOSetup
               bcs       ex@
* Determine if this device is ATAPI or ATA
               bitb      #$02                ATAPI?
               lbeq      ATADSize            no, it's ATA
* Note - for ATAPI version of SS.DSize, we use the obsolete
* READ CAPACITY call because it works on CD-ROMs, as opposed to
* READ FORMAT CAPACITIES.
ATAPIDSize               
               lbsr      ATAPIPreSend        prepare packet
* Populate packet buffer with STOP code and Eject
*         ldd   #$230C		ATAPI READ FORMAT CAPACITIES Code
               lda       #$25                ATAPI READ CAPACITY Code
               sta       V.ATAPICmd,u        write it
*         stb   V.ATAPICmd+8,u	and allocation length
* Send to data port
               bsr       ATAPISend           send command
               bcs       ex@
* Read 8 bytes of format capacity data
*         ldb   #6
               ldb       #4
               pshs      b
               leay      V.SnsData,u
read@          lda       DataReg,x
               ldb       Latch,x
               std       ,y++
               dec       ,s
               bne       read@
               puls      b
               ldx       V.SnsData+0,u
               ldy       V.SnsData+2,u
               leay      1,y
               bcc       b@
               leax      1,x
b@             lda       V.SnsData+6,u
               clrb      
ex@            rts       

*         ldy   PD.RGS,y
*         ldd   V.SnsData+0,u	get bits 31-16
*         std   R$X,y
*         ldd   V.SnsData+2,u	get bits 15-0
*         addd  #$0001		add 1
*         std   R$Y,y
*         bcc   b@
*         ldd   R$X,y
*         addd  #$0001
*         std   R$X,y
*b@       lda   V.SnsData+6,u	get bits 15-8 of block size
*         sta   R$A,y
*         clr   R$B,y		signal that this is LBA mode
*         clrb
*ex@      rts        


* ll_getstat - Low level GetStat routine
*
* Entry:
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
ll_getstat               
               ldx       PD.RGS,y
               lda       R$B,x
               cmpa      #SS.DSize
               beq       SSDSize
               ldb       #E$UnkSvc
               coma      
ex1            rts       


* StopUnit - Park a drive
*
* ATA   Devices: This is a No-Op.
* ATAPI Devices: A STOP UNIT command is issued to the device.
*                (ejects media on ATAPI removable devices)
StopUnit       lbsr      IOSetup
               bcs       ex1
* Determine if this device is ATAPI or ATA
               bitb      #$02                ATAPI?
               beq       ex1                 no, ignore...
               lbsr      ATAPIPreSend        prepare packet
* Populate packet buffer with STOP code and Eject
ok@            ldd       #A$STOP*256+$02     ATAPI STOP Code and Eject byte
               sta       V.ATAPICmd,u        write it and RSV to zero
               stb       V.ATAPICmd+4,u
* Send to data port
               bra       ATAPISend           send command


* ll_setstat - Low level SetStat routine
*
* Entry:
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
ll_setstat               
               ldx       PD.RGS,y
               lda       R$B,x
               cmpa      #SS.SQD
               beq       StopUnit
               IFNE      0
               cmpa      #SS.DCmd
               bne       n@
               pshs      x                  save pointer to caller registers
               bsr       DCmd               call DCmd
               puls      x                  get pointer to caller registers
               sta       R$A,x              save status byte in A
               ENDC
n@             clrb      
ssex           rts       


               IFNE    0
BadType        comb
               ldb     #E$BTyp
               rts

* Entry:
*    X   = caller regs
*    Y   = path descriptor
*
*    R$B = SS.DCmd
*    R$X = Transfer buffer
*    R$Y = ATAPI command packet
DCmd                     
               pshs      y
               os9       F$ID                get the user ID of the calling process
               cmpy      #$0000              is it 0 (superuser)?
               puls      y
               bne       noperms             no, don't allow the call
               lbsr      IOSetup
** SS.DCmd only works with ATAPI devices.
               bitb      #$02                ATAPI?
               beq       BadType             branch if not
               ldy       R$X,x               get caller's transfer buffer
               sty       V.UTxBuf,u          save off in mem for later
               ldx       R$Y,x               get ptr to caller's command buffer
               IFGT      Level-1
               ldy       D.Proc              get current process ptr
               lda       P$Task,y            get task # for current process
               ldb       D.SysTsk            get system task #
               ldy       #ATAPIPkLn          max size of ATAPI command
               pshs      u                   save on stack
               leau      V.ATAPICmd,u        point to ATAPI command buffer in our statics
               os9       F$Move              copy from caller to temporary task
               puls      u
               bcs       ex                  error copying, exit
               ELSE      
               ldb       #ATAPIPkLn
               leay      V.ATAPICmd,u
cl@            lda       ,x+
               sta       ,y+
               decb      
               bne       cl@
               ENDC      
               ldy       V.PORT-UOFFSET,u    get hw address (because we overwrite Y earlier)
*               inc       V.OS9Err,u          we want real errors returned
               inc       V.CchDirty,u        and make cache dirty
*               leax      retry@,pcr
*               stx       V.RetryVct,u
retry@         lbsr      ATAPISend
               bcs       ex
               IFGT      Level-1
               ldx       D.Proc              get current process ptr
               ldb       P$Task,x            get task # for current process
               ENDC      
               ldx       V.UTxBuf,u

msgloop@       lbsr      Wait4REQ            wait for REQ to be asserted
               bita      #CMD                command phase?
               lbne      PostXfr             yes, return
io@            bita      #INOUT              data coming in or going out?
               bne       in@                 branch if coming in...
               IFGT      Level-1
               os9       F$LDABX
               leax      1,x
               ELSE      
               lda       ,x+
               ENDC      
               sta       SCSIDATA,y
               bra       msgloop@
in@            lda       SCSIDATA,y
               IFGT      Level-1
               os9       F$STABX
               leax      1,x
               ELSE      
               sta       ,x+
               ENDC      
               bra       msgloop@
               ENDC

* ATAPISend - Sends the command packet to the device
*
* Entry:   X = HW address
*          V.WhichDv = DevHead device selection value
* Exit:    Carry = 1; error code in B
*          Carry = 0; command successfully sent
ATAPISend                
* First, select the device and wait for /BUSY
               lda       V.WhichDv,u
               sta       DevHead,x           select device
* ATAPI says we wait for !BUSY
               ldd       #NBUSYDRDY          wait for NBUSY and DEVREADY too - CRH
               lbsr      StatusWait          wait for proper condition
               bcs       timeout             branch if error
               lda       #NumRetries         get retry count
               sta       V.Retries,u         and save
retry@                   
               clr       Features,x          clear feature byte
               clr       SectCnt,x           clear TAG field
               ldd       #$FFFF              maximum read in PIO mode
               std       CylLow,x
               lda       #$A0                ATAPI PACKET CODE
               sta       Command,x           write it to device
* Check for error
               ldd       #NBUSYDRQ           /BUSY and DRQ
               lbsr      StatusWait          wait for proper condition
               bcs       ex@                 branch if error
* Send to data port
               lda       #6                  packet size / 2
               pshs      a,y
               leay      V.ATAPICmd,u
l@             ldd       ,y++
               stb       Latch,x
               sta       DataReg,x
               dec       ,s
               bne       l@
               puls      a,y
* Added by CRH - Some drives require that we wait for much more than
* 400ns.  Hence this code forces a slow-down and checks the status
* to see if things are ok.
               clrb      
slow@          decb                          CRH wait for much more than 400ns
               bne       slow@
               lda       Status,x            CRH Sometimes error on first try
               lsra      
               bcc       ok@
               dec       V.Retries,u
               bne       retry@
* As per ATA/ATAPI-6 spec (T13/1410D Revision 3A), page 161, we wait for /BUSY
* and DRQ after sending a command
* That IS NOT the case when sending START/STOP unit commands, so we don't check
* for DRQ.
ok@            ldd       #NBUSY              /BUSY
               lbsr      StatusWait          wait for proper condition
               bcs       ex@                 branch if error
               lda       #A$STOP             CRH see above
               cmpa      V.ATAPICmd,u
               beq       nodata@
               ldd       #NBUSYDRQ           /BUSY and DRQ
               lbsr      StatusWait          wait for proper condition
               bcs       ex@                 branch if error
nodata@        lsra                          shift in CHECK CONDITION bit
               bcc       ex@                 branch if ok
* Note: if the device returns error, DRQ won't be set...
err@           lbsr      HandleSenseKey      go check sense key
               bcs       ex@
ret@           dec       V.Retries,u         else retry until terminal
               bne       retry@              continue retrying
timeout        comb                          set carry
               ldb       #E$DevBsy+4         SHOULD BE AN E$TimeOut ERROR!
ex@            rts       

* Prepare the ATAPI Packet
ATAPIPreSend             
               pshs      x
* Clear 12 byte packet.
               leax      V.ATAPICmd,u
               ldb       #12
c@             clr       ,x+
               decb      
               bne       c@
ex@            puls      x,pc


** ATAPI REQUEST SENSE Command
** Should only be called for ATAPI devices
*ReqSense 
*         bsr    ATAPIPreSend	prepare packet
** Populate packet buffer with REQUEST SENSE
*         ldd   #$0312		ATAPI REQUEST SENSE Code and allocation length byte
*         sta   V.ATAPICmd,u
*         stb   V.ATAPICmd+4,u
** Send to data port
*         lbsr  ATAPISend	send command
*         lda   Status,x		get status code
*         lsra			shift in CHECK CONDITION bit
*         bcs   ex@		branch if ok
** Read 18 bytes of sense data
*         ldb   #$12
*         pshs  y,b
*         leay  V.SnsData,u
*read@    lda   DataReg,x 
*         ldb   Latch,x   
*         std   ,y++      
*         dec   ,s        
*         bne   read@     
*         puls  b,y,pc
*ex@      rts


* IOSetup - Sets up the device for I/O
*
* The device is selected (master or slave), then the device is
* checked for previous initialization.
*
* If the device has not been initialized, it is queried for its
* mode (ATAPI/ATA, LBA or CHS) and size.  That information is
* saved in the driver's static storage for later use by other
* routines.
*
* Entry:  Y = path descriptor pointer
*         U = static memory pointer
* Exit:   B = status byte for device
*         X = HW address
*         Y = pointer to current device table
IOSetup        ldx       V.PORT-UOFFSET,u    get hw address
               lda       PD.DNS,y            get device ID bit
               lsra                          shift device ID into carry
               bcs       slave@
               lda       #%10100000          master byte
               leay      V.Master,u
               bra       t@
slave@         lda       #%10110000          slave byte
               leay      V.Slave,u           else point to slave status byte
t@             sty       V.CurDTbl,u
               sta       V.WhichDv,u         save for later
* Select the device -- on power-up, the status register is usually 0
               sta       DevHead,x           select device
* According to page 320 of the ATA/ATAPI-6 document, we must wait for BOTH
* BUSY and DRQ to be clear before proceeding. (HI2: Device_Select State)
               ldd       #NBUSYNDRQ          /BUSY and /DRQ
               lbsr      StatusWait          wait for proper condition
               lbcs      ex@                 branch if error
* Determine if this device has already been initialized
               tst       ,y                  test device's stat byte
               lbne      initdone            if not zero, init already done
* Here we must initialize the device by IDENTIFYING it.
* First, try sending the ATA IDENTIFY DRIVE code
               lda       #$EC                ATA identify command
               sta       Command,x           write it
               ldd       #NBUSY              /BUSY
               lbsr      StatusWait          wait for proper condition
               lbcs      ex@                 branch if error
* Check if there's an error
               lsra                          shift error bit into carry
               bcc       ATAIdent            if no error, then probably ATA
* If here, we got an error sending $EC, so try ATAPI's $A1
               lda       #$A1
               sta       Command,x
               clrb      
slow@          decb                          CRH wait for much more than 400ns
               bne       slow@
               ldd       #NBUSY
               lbsr      StatusWait          wait for proper condition
               bcs       ex@                 branch if error
               lsra                          shift error bit into carry
               bcs       timeout             if not error, we're ok
               ldd       #NBUSYDRQ
               lbsr      StatusWait          wait for proper condition
               lbcs      ex@                 branch if error
* Here, we have identified an ATAPI device.
ATAPIIdent               
               ldb       #$03                ATAPI
               stb       ,y
* We flush the ATAPI data but don't reference it
dread@         ldb       DataReg,x           CRH flush ALL bytes
*         ldb   Latch,x 	but save time by not reading latch
               lda       Status,x
               anda      #8
               bne       dread@
               bra       initdone
* ATAIdent - process an ATA device
* This routine is called by IOSetup when it deduces that the device
* being queried is an ATA device.  This device is called ONCE -- the
* first time the device is accessed.
* This routine will set up our per-drive static storage to indicate that
* it is an ATA device.  It will also determine if it is an LBA or CHS mode
* device, and save the appropriate CHS or LBA sector values.
ATAIdent                 
               ldd       #NBUSYDRDY          /BUSY and DRDY
               lbsr      StatusWait          wait for proper condition
               bcs       ex@                 branch if error
* Harvest C/H/S and LBA sector values.
               ldb       DataReg,x           ignore bytes 0-1
               ldb       DataReg,x           bytes 2-3 = no. of cylinders
               lda       Latch,x
               std       1,y                 save cylinders in our private static area
               ldb       DataReg,x           ignore bytes 4-5
               ldb       DataReg,x           bytes 6-7 = no. of heads
               lda       Latch,x
               stb       3,y                 save sides on stack (B)
               ldb       DataReg,x           ignore bytes 8-9
               ldb       DataReg,x           ignore bytes 10-11
               ldb       DataReg,x           bytes 12-13 = no. of sectors/track
               lda       Latch,x
               std       4,y                 save sectors/track on stack (Y)
* Throw away the next 42 (7-48) words
               ldb       #43
l@             tst       DataReg,x
               lda       Latch,x
               decb      
               bne       l@
* A holds byte with LBA bit
               incb                          B was 0, now 1
               anda      #%00000010          LBA allowed on this drive?
               beq       nope@
               orb       #$04                set LBA mode
               stb       ,y                  save updated status byte
* Since we're LBA mode, get the number of LBA sectors in words 60-61
               ldb       #10                 skip to the LBA sectors (words 60-61)
more@          tst       DataReg,x           simply read the data register like this...
               decb      
               bne       more@
               ldb       DataReg,x           get word 60
               lda       Latch,x
               std       3,y
               ldb       DataReg,x           and 61
               lda       Latch,x
               std       1,y
               lda       #256-61             how many words we have left
               bra       left@               go on.
nope@          stb       ,y                  save updated status byte
* Read remaining 256-50 words
               lda       #256-50
left@          ldb       DataReg,x
               deca      
               bne       left@
initdone       ldb       ,y                  get status byte of drive
               clra                          clear carry
ex@            rts       


* ATAPI Write Routine -- Independent of ATA Read
ATAPIWrite               
               lbsr      ATAPIPreSend        prepare packet
* Populate packet buffer with WRITE code and sector information
again@         ldb       V.PhySct,u          get bits 23-16 of sector
               stb       V.ATAPICmd+3,u
               ldd       V.PhySct+1,u        get bits 15-0 of sector
               std       V.ATAPICmd+4,u
               ldd       #WRITCODE*256+$01   ATAPI WRITE Code and transfer length
               sta       V.ATAPICmd,u        write it
               IFNE      RW12
               stb       V.ATAPICmd+9,u      write to byte 9
               ELSE      
               stb       V.ATAPICmd+8,u      write to byte 8
               ENDC      
* Send to data port
               lbsr      ATAPISend           send command
               bcs       ex@
* Shift data from device
o@             pshs      d
               lda       V.Log2Phys,u
               sta       1,s                 set up our logical sector counter
inc@           clr       ,s                  set up our byte counter
wr@            ldd       ,y++
               stb       Latch,x
               sta       DataReg,x
               inc       ,s
               bpl       wr@
               dec       1,s
               bne       inc@
               puls      d
* Increment physical sector
               inc       V.PhySct+2,u
               bcc       go@
               inc       V.PhySct+1,u
               bcc       go@
               inc       V.PhySct,u
go@            dec       V.SctCnt,u          decrement # of hw sectors to read
               bne       again@              if not zero, do it again
               ldd       #NBUSY              /BUSY
               lbsr      StatusWait          wait for proper condition
               bcs       ex@                 branch if error
               lsra                          error bit set?
               bcc       ex@                 yep...
               bsr       HandleSenseKey
ex@            puls      x,pc



* ATAPI Read Routine -- Independent of ATA Read
ATAPIRead                
               ldy       V.CchPSpot,u        get pointer to spot in cache to put sector
               lbsr      ATAPIPreSend        do command packet setup stuff
* Populate packet buffer with READ code and sector information
again@         ldb       V.PhySct,u          get,u bits 23-16 of sector
               stb       V.ATAPICmd+3,u
               ldd       V.PhySct+1,u        get bits 15-0 of sector
               std       V.ATAPICmd+4,u
               ldd       #READCODE*256+$01   ATAPI Read Code and transfer length
               sta       V.ATAPICmd,u        write it and RSV to zero
               IFNE      RW12
               stb       V.ATAPICmd+9,u      write to byte 9
               ELSE      
               stb       V.ATAPICmd+8,u      write to byte 8
               ENDC      
* Send to data port
               lbsr      ATAPISend           send command
               bcs       ex@
* Shift data from device
o@             pshs      d
               lda       V.Log2Phys,u
               sta       1,s                 set up our logical sector counter
inc@           clr       ,s                  set up our byte counter
read@          lda       DataReg,x
               ldb       Latch,x
               std       ,y++
               inc       ,s
               bpl       read@
               dec       1,s
               bne       inc@
               puls      d
* Increment physical sector
               inc       V.PhySct+2,u
               bcc       go@
               inc       V.PhySct+1,u
               bcc       go@
               inc       V.PhySct,u
go@            dec       V.SctCnt,u          decrement # of hw sectors to read
               bne       again@              if not zero, do it again
               ldd       #NBUSY              /BUSY
               lbsr      StatusWait          wait for proper condition
               bcs       ex@                 branch if error
               lsra                          error bit set?
               bcc       ex@                 nope...
               bsr       HandleSenseKey
ex@            puls      x,pc


* Handle ATAPI Sense Key
* If the resulting error in the look-up table is zero,
* we return with carry clear
* Returns: B =  0 (carry clear, no error)
*          B != 1 (carry set, error)
HandleSenseKey           
               pshs      x,a
               ldb       ErrorReg,x          get error register value
               lsrb                          shift sense key into place
               lsrb      
               lsrb      
               lsrb      
               leax      SenseMap,pcr        point to Sense Key Map
               clra                          clear carry
               ldb       b,x                 get appropriate error
               beq       ok@                 if error is zero, return ok
               coma                          set carry
ok@            puls      a,x,pc

* ll_read - Low level read routine
*
* Entry:
*    Registers:
*      Y  = address of path descriptor
*      U  = address of device memory area
*    Static Variables of interest:
*      V.PhySct = starting physical sector to read from
*      V.SectCnt  = number of physical sectors to read
*      V.SectSize = physical sector size (0=256,1=512,2=1024,3=2048)
*      V.CchPSpot = address where physical sector(s) will go
*
* Exit:
*    All registers may be modified
*    Static variables may NOT be modified
ll_read                  
               pshs      x                   make some space on the stack
               lbsr      IOSetup             initialize the device
               lbcs      ex@
* Copy V.PhySct and V.SectCnt to our local copy
* since we cannot modify them.
               lda       V.PhysSect,u
               ldy       V.PhysSect+1,u
               sta       V.PhySct,u
               sty       V.PhySct+1,u
               lda       V.SectCnt,u
               sta       V.SctCnt,u
               bitb      #$02                ATAPI device?
               lbne      ATAPIRead           yes, go do it
* ATA Read Routine
ATARead                  
* stb   V.CurStat,u	save status of current drive
               bitb      #$04                LBA drive?
               bne       lba@                branch if so
               leay      DoCHS,pcr           else point Y to CHS routine
               bra       skip@
lba@           leay      DoLBA,pcr
skip@          sty       V.ATAVct,u          save pointer
               ldy       V.CchPSpot,u        get pointer to spot in cache to put sector
loop@                    
               ldd       #NBUSY              /BUSY
               bsr       StatusWait          wait for proper condition
               bcs       ex@                 branch if error
               jsr       [V.ATAVct,u]        do proper ATA preparation
               bcs       ex@                 branch if error
cont@          lda       #$01
               sta       SectCnt,x           store it
               lda       #S$READ
               sta       Command,x
               ldd       #NBUSY              /BUSY
               bsr       StatusWait          wait for proper condition
               bcs       ex@                 branch if error
               lsra                          error bit set?
               bcc       w@                  branch if not
               lbsr      ATAError
               bra       ex@
w@             ldd       #NBUSYDRQ           /BUSY and DRQ
               bsr       StatusWait          wait for proper condition
               bcs       ex@                 branch if error
               lda       V.Log2Phys,u
               sta       1,s                 set up our logical sector counter
inc@           clr       ,s                  set up our byte counter
read@          lda       DataReg,x
               ldb       Latch,x
               std       ,y++
               inc       ,s
               bpl       read@
               dec       1,s
               bne       inc@
* Increment physical sector
               inc       V.PhySct+2,u
               bcc       go@
               inc       V.PhySct+1,u
               bcc       go@
               inc       V.PhySct,u
go@            dec       V.SctCnt,u          decrement # of hw sectors to read
               bne       loop@               if not zero, do it again
               clrb      
ex@            puls      x,pc


*
* Convert LSN to LBA values
*
* Entry:  V.PhySct = bits 23-0 of LSN
*         X      = ptr to hardware
*
* Exit:   CHS values placed directly in HW
*
DoLBA          lda       V.WhichDv,u         get devhead value populated by IOSetup (CHS mode)
               ora       #%01000000          OR in LBA bit
               sta       DevHead,x
               ldd       #NBUSYNDRQ          /BUSY and /DRQ
               bsr       StatusWait
               bcs       ex@
               ldb       V.PhySct,u          get bits 23-16 of sector
               stb       CylHigh,x           store it
               ldd       V.PhySct+1,u        get bits 15-0 of sector
               stb       SectNum,x           store it
               sta       CylLow,x
ex@            rts       


* Wait for a set of conditions in the status register to be TRUE
* This yields a delay of about 4 seconds.
*
* Entry: X = HW address
*        A = flip (if bit set, that bit is tested)
*        B = mask (result must match this byte)
* Exit:  A = status
StatusWait               
               pshs      y,b,a
               IFEQ      Level-1
               ldb       #WAITTIME/2
               ldy       #$0000
               ELSE      
               ldb       #WAITTIME
               ldy       #$0000
               ENDC      
l@             lda       Status,x
               anda      ,s                  apply flip
               cmpa      1,s                 compare to mask
               bne       dec@                branch if not equal (not what we want)
               clrb                          clear carry
               bra       ok@
dec@           leay      -1,y                count down
               bne       l@
               decb                          decrement bits 23-16
               bpl       l@                  if >=0, keep going
err@           comb                          set carry
               ldb       #E$DevBsy
ok@            leas      2,s
               lda       Status,x            get status again
               puls      y,pc

* Wait for 1 tick (1/60 second)
*Delay1Tk
*         pshs  x
*         IFGT  Level-1
*         ldx   D.Proc		get proc descriptor
*         cmpx  D.SysPrc	system?
*         beq   hw@		yep, system cannot sleep
*         ENDC
*         ldx   D.AProcQ	get active proc queue
*         beq   hw@		if empty, do hard wait
*         ldx   #1
*         os9   F$Sleep		give up worst case: 1 tick (1/60 second)
*         puls  x,pc		return to caller
** In case we can't sleep... do a hard 1/60 second delay
*hw@ 
*         IFEQ  Level-1
*         ldx   #$E52E/2		(5) (4)
*         ELSE
*         ldx   #$E52E		(5) (4)
*         ENDC
*w@       leax  -1,x		(4+) (4+)
*         bne   w@		(3) (3)
*         puls  x,pc		return to caller
*


* ll_write - Low level write routine
*
* Entry:
*    Registers:
*      Y  = address of path descriptor
*      U  = address of device memory area
*    Static Variables of interest:
*      V.PhySct = starting physical sector to write to
*      V.SectCnt  = number of physical sectors to write
*      V.SectSize = physical sector size (0=256,1=512,2=1024,3=2048)
*      V.CchPSpot = address of data to write to device
*
* Exit:
*    All registers may be modified
*    Static variables may NOT be modified
ll_write                 
               pshs      x                   make some space on the stack
               lbsr      IOSetup             initialize the device
               lbcs      ex@
* Copy V.PhySct to our local copy
               lda       V.PhysSect,u
               ldy       V.PhysSect+1,u
               sta       V.PhySct,u
               sty       V.PhySct+1,u
               lda       V.SectCnt,u
               sta       V.SctCnt,u
*
               ldy       V.CchPSpot,u        get pointer to spot in cache where physical sector is
               bitb      #$02                ATAPI device?
               lbne      ATAPIWrite          yes, go do it
* ATA Write Routine
ATAWrite       stb       V.CurStat,u         save status of current drive
loop@                    
               ldd       #NBUSY              /BUSY
               bsr       StatusWait          wait for proper condition
               bcs       ex@                 branch if ok
* Check for LBA mode
               ldb       V.CurStat,u         get status of current drive
               bitb      #$04                LBA bit set?
               bne       lba@                branch if so
* Here, we use CHS
               bsr       DoCHS
               bcs       ex@
               bra       cont@
lba@           lbsr      DoLBA
cont@          lda       #$01
               sta       SectCnt,x           store it
               lda       #S$WRITE
               sta       Command,x
               ldd       #NBUSY              /BUSY
               bsr       StatusWait          wait for proper condition
               bcs       ex@                 branch if ok
               lsra                          error bit set?
               bcc       g@                  branch if not
               lbsr      ATAError
               bra       ex@
g@             ldd       #NBUSYDRQ           /BUSY and DRQ
               lbsr      StatusWait          wait for proper condition
               bcs       ex@                 branch if ok
again@         lda       V.Log2Phys,u
               sta       1,s                 set up our sector counter
inc@           clr       ,s                  set up our byte counter
wr@            ldd       ,y++
               stb       Latch,x
               sta       DataReg,x
               inc       ,s
               bpl       wr@
               dec       1,s
               bne       inc@
* Increment physical sector
               inc       V.PhySct+2,u
               bcc       go@
               inc       V.PhySct+1,u
               bcc       go@
               inc       V.PhySct,u
go@                      
               dec       V.SctCnt,u          decrement # of hw sectors to read
               bne       loop@               if not zero, do it again
               clrb      
ex@            puls      x,pc

*
* Convert LSN to C/H/S values and write to IDE hardware
*
* Entry:  V.PhySct = bits 23-0 of LSN
*         X      = ptr to hardware
*
* Exit:   CHS values placed directly in HW
*
DoCHS                    
* Select device
               lda       V.WhichDv,u         get devhead value made by IOSetup (already CHS)
               sta       DevHead,x
               ldd       #NBUSYNDRQ          /BUSY and /DRQ
               lbsr      StatusWait          wait for proper condition
               bcc       start@              branch if ok
               rts       
* Start computation
start@         pshs      y                   save original Y
               ldy       V.CurDTbl,u
               lda       3,y                 get device's head
               ldb       5,y                 and sector
               stb       V.Sectors,u
               mul                           multiply H*S
               beq       ZeroProd            if zero, error out
               pshs      d                   save product of H*S
               ldd       V.PhySct+1,u        get bits 15-0 of LSN
               ldy       #-1                 start Y at -1
               inc       V.PhySct,u          increment physical sector
* Here we are doing physLSN/(H*S) to get cylinder for physLSN
a@             leay      1,y                 increment count to compensate
               subd      ,s                  subtract (H*S) from physLSN
               bhs       a@                  if D>=0 then continue
               dec       V.PhySct,u          decrement phys sector bits 23-16
               bne       a@                  if not zero, continue divide
               addd      ,s++                add in (H*S) to make non-negative
               pshs      d                   D now holds cylinder, save on stack 
               tfr       y,d                 Y now holds cylinder value
               exg       a,b                 swap
               std       CylLow,x            store computed cylinder in HW
               puls      d                   restore saved cylinder
* Now we will compute the sector/head value
               ldy       #-1
b@             leay      1,y
               subb      V.Sectors,u
               sbca      #0
               bcc       b@
               addb      V.Sectors,u
               incb                          add 1 to B, which is sector
               stb       SectNum,x           store computed sector in HW
               tfr       y,d
               orb       DevHead,x           OR in with value written earlier
               stb       DevHead,x
               clrb      
               puls      y,pc
ZeroProd       ldb       #E$Sect
               coma      
               puls      y,pc

* ATAError - Checks the ATA error register and maps
*            to a NitrOS-9 error message.
*
* Called if the error bit in the status register is set.
ATAError                 
               lda       ErrorReg,x
               ldb       #7
l@             lsra      
               bcs       LookUp
               decb      
               bne       l@
LookUp         leax      Errs,pcr
x@             ldb       b,x
               coma      
               rts       

* This is the ATAPI Sense Key -> NitrOS-9 Error Table
* The Sense Key Table is on page 50 of the ATAPI Removable
* Rewritable Specification, Revision 1.3 Proposed.
* If an error number is zero, then no error is returned.
SenseMap       FCB       0                   sense key 0 (NO SENSE)
               FCB       0                   sense key 1 (RECOVERED ERROR)
               FCB       E$NotRdy            sense key 2 (NOT READY)
               FCB       E$Sect              sense key 3 (MEDIUM ERROR)
               FCB       E$Unit              sense key 4 (HARDWARE ERROR)
               FCB       E$IllArg            sense key 5 (ILLEGAL REQUEST)
               FCB       0                   sense key 6 (UNIT ATTENTION)
               FCB       E$WP                sense key 7 (DATA PROTECT)
               FCB       0                   sense key 8 (BLANK CHECK)
               FCB       0                   sense key 9 (VENDOR SPECIFIC)
               FCB       0                   sense key A (RESERVED)
               FCB       1                   sense key B (ABORTED COMMAND)
               FCB       0                   sense key C (RESERVED)
               FCB       0                   sense key D (VOLUME OVERFLOW)
               FCB       0                   sense key E (MISCOMPARE)
               FCB       0                   sense key F (RESERVED)

* ERROR REG Bit   0      1     2       3       4        5      6     7
Errs           FCB       E$Unit,E$CRC,E$UnkSvc,E$Sect,E$UnkSvc,E$DIDC,E$Seek,E$Sect

               EMOD      
eom            EQU       *
               END       
