;CF140A
          .TITLE    "Apple /// CompactFlash/IDE Driver Ver 1.40A"
                    ;Limit Maximum Volume Size to 32767 Blocks

          .PROC     CFIDE
;
; SOS Equates
;
ExtPG     .EQU      1401             ;driver extended bank address offset
AllocSIR  .EQU      1913             ;allocate system internal resource
SELC800   .EQU      1922             ;Enable Expansion Rom Space
DeAlcSIR  .EQU      1916             ;de-allocate system internal resource
SysErr    .EQU      1928             ;report error to system
EReg      .EQU      0FFDF            ;environment register
ReqCode   .EQU      0C0              ;request code
SOS_Unit  .EQU      0C1              ;unit number
SosBuf    .EQU      0C2              ;SOS buffer pointer
ReqCnt    .EQU      0C4              ;requested byte count
CtlStat   .EQU      0C2              ;control/status code
CSList    .EQU      0C3              ;control/status list pointer
SosBlk    .EQU      0C6              ;starting block number
QtyRead   .EQU      0C8              ;bytes read return by D_READ
;
; Our temps in zero page
;
CurPart   .EQU      0CC              ;1 byte
Count     .EQU      0CD              ;2 bytes
Validate  .EQU      0CE              ;1 byte
;
; Parameter block specific to current SOS request
;
ATA_Cmd   .EQU      0CF
Drv_Parm  .EQU      0D0
Sect_HB   .EQU      0D1
Sect_MB   .EQU      0D2
Sect_LB   .EQU      0D3
Num_Blks  .EQU      0D4             ;2 bytes lb,hb
DataBuf   .EQU      0D6             ;2 bytes
CurDrive  .EQU      0D8             ;Current IDE drive number of SOS_Unit #
CurDrvNo  .EQU      0DA             ;Current DIB drive number of SOS_Unit #
;
; SOS Error Codes
;
XREQCODE  .EQU      020              ;Invalid request code
XCTLCODE  .EQU      021              ;Invalid control/status code
XCTLPARAM .EQU      022              ;Invalid control/status parameter
XNORESRC  .EQU      025              ;Resource not available
XBADOP    .EQU      026              ;Invalid operation
XIOERROR  .EQU      027              ;I/O error
XNODRIVE  .EQU      028              ;Drive not connected
XBYTECNT  .EQU      02C              ;Byte count not a multiple of 512
XBLKNUM   .EQU      02D              ;Block number to large
XDCMDERR  .EQU      031              ;device command ABORTED error occurred
XCKDEVER  .EQU      032              ;Check device readiness routine failed
XNORESET  .EQU      033              ;Device reset failed
XNODEVIC  .EQU      038              ;Device not connected
;
; IFC I/O locations
;
IFC_ID    .EQU      0C820
IOBase    .EQU      0C080
ATdataHB  .EQU      IOBase+0.
SetCSMsk  .EQU      IOBase+1.        ;Two special strobe locations to set and
                                     ;clear MASK bit that is used to disable
                                     ;CS0 line to the CompactFlash during the
                                     ;CPU read cycles.
ClrCSMsk  .EQU      IOBase+2.        ;that occur before every CPU write cycle.
                                     ;The normally innocuous read cycles were
                                     ;causing the SanDisk CF to double
                                     ;increment during sector writes commands.
Alt_Stat  .EQU      IOBase+6.        ;when reading (not used)
ATAdCtrl  .EQU      IOBase+6.        ;when writing
ATdataLB  .EQU      IOBase+8.
ATAError  .EQU      IOBase+9.
Features  .EQU      IOBase+9.
ATSectCt  .EQU      IOBase+10.
ATSector  .EQU      IOBase+11.       ;11=LB,12=MB,13=HB
ATAHead   .EQU      IOBase+14.
ATCmdReg  .EQU      IOBase+15.       ;when writing
ATA_Stat  .EQU      IOBase+15.       ;when reading
;
; ATA/CF Commands Codes
;
ATA_XErr  .EQU      003
ATACRead  .EQU      020
ATACWrit  .EQU      030
ATA_Vrfy  .EQU      040
ATA_Frmt  .EQU      050
ATA_Diag  .EQU      090
ATAIdent  .EQU      0EC
SetFeatr  .EQU      0EF
;
; Constants for Wait
;
Wait5ms   .EQU      42.
Wait150ms .EQU      242.
;
; Switch Macro
;
          .MACRO    switch
          .IF       "%1" <> ""       ;if parameter 1 is present
          LDA       %1               ;load A with switch index
          .ENDC
          CMP       #%2+1            ;do bounds check
          BCS       $010
          ASL       A
          TAY
          LDA       %3+1,Y           ;get switch index from table
          PHA
          LDA       %3,Y
          PHA
          .IF       "%4" <> "*"      ;if parameter 4 omitted,
          RTS       ;go to code
          .ENDC
$010      .ENDM
;
; Comment Field of driver
;
          .WORD     0FFFF
          .WORD     83.
          .ASCII    "Apple /// CFFA Driver - "
          .ASCII    "written by Dale S. Jackson 8/08"
          .ASCII    ", modified by D Schmenk 8/11"
;
; DIB Values for Map & Drive No.
;
; Map No.   IDE $0   IDE $1     Map No.   IDE $0    IDE $1
;   0       $00       $01        32        $80        $81
;   1       $04       $05        33        $84        $85
;   2       $08       $09        34        $88        $89
;   3       $0C       $0D        35        $8C        $8D
;   4       $10       $11        36        $90        $91
;   5       $14       $15        37        $94        $95
;   6       $18       $19        38        $98        $99
;   7       $1C       $1D        39        $9C        $9D
;   8       $20       $21        40        $A0        $A1
;   9       $24       $25        41        $A4        $A5
;  10       $28       $29        42        $A8        $A9
;  11       $2C       $2D        43        $AC        $AD
;  12       $30       $31        44        $B0        $B1
;  13       $34       $35        45        $B4        $B5
;  14       $38       $39        46        $B8        $B9
;  15       $3C       $3D        47        $BC        $BD
;  16       $40       $41        48        $C0        $C1
;  17       $44       $45        49        $C4        $C5
;  18       $48       $49        50        $C8        $C9
;  19       $4C       $4D        51        $CC        $CD
;  20       $50       $51        52        $D0        $D1
;  21       $54       $55        53        $D4        $D5
;  22       $58       $59        54        $D8        $D9
;  23       $5C       $5D        55        $DC        $DD
;  24       $60       $61        56        $E0        $E1
;  25       $64       $65        57        $E4        $E5
;  26       $68       $69        58        $E8        $E9
;  27       $6C       $6D        59        $EC        $ED
;  28       $70       $71        60        $F0        $F1
;  29       $74       $75        61        $F4        $F5
;  30       $78       $79        62        $F8        $F9
;  31       $7C       $7D        63        $FC        $FD

;------------------------------------
;
; Device identification Block (DIB) - Volume #0
;
;------------------------------------

DIB_0     .WORD     DIB_1            ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE1        ";device name
;          .BYTE     0C0              ;active, page aligned
          .BYTE     080              ;active, no page alignment
DIB0_Slot .BYTE     001  ;0FF        ;slot number
          .BYTE     000              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB0_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     140A             ;Version 1.40A
          .WORD     0003             ;DCB length followed by DCB
Driv_No0  .BYTE     000              ;Drive #, Bit 0=$0 (master) or $1 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
Part_No0  .BYTE     000              ;Partition number on the drive = $00-07
FastXfer  .BYTE     000              ;Fast transfer flag
;
; Device identification Block (DIB) - Volume #1
; Page alignment begins here
;
DIB_1     .WORD     DIB_2            ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE2        ";device name
          .BYTE     080              ;active
          .BYTE     001  ;0FF        ;slot number
          .BYTE     001              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB1_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     140A             ;Version 1.40A
          .WORD     0002             ;DCB length followed by DCB
          .BYTE     000              ;Drive # = $00 (master) or $01 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
          .BYTE     001              ;Partition number on the drive = $00-07
;
; Device identification Block (DIB) - Volume #2
;
DIB_2     .WORD     DIB_3            ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE3        ";device name
          .BYTE     080              ;active
          .BYTE     001  ;0FF        ;slot number
          .BYTE     002              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB2_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     140A             ;Version 1.40A
          .WORD     0002             ;DCB length followed by DCB
          .BYTE     000              ;Drive # = $00 (master) or $01 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
          .BYTE     002              ;Partition number on the drive = $00-07
;
; Device identification Block (DIB) - Volume #3
;
DIB_3     .WORD     DIB_4            ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE4        ";device name
          .BYTE     080              ;active
          .BYTE     001  ;0FF        ;slot number
          .BYTE     003              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB3_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     140A             ;Version 1.40A
          .WORD     0002             ;DCB length followed by DCB
          .BYTE     000              ;Drive # = $00 (master) or $01 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
          .BYTE     003              ;Partition number on the drive = $00-07
;
; Device identification Block (DIB) - Volume #4
;
DIB_4     .WORD     DIB_5            ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE5        ";device name
          .BYTE     080              ;active
          .BYTE     001  ;0FF        ;slot number
          .BYTE     004              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB4_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     140A             ;Version 1.40A
          .WORD     0002             ;DCB length followed by DCB
          .BYTE     000              ;Drive # = $00 (master) or $01 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
          .BYTE     004              ;Partition number on the drive = $00-07
;
; Device identification Block (DIB) - Volume #5
;
DIB_5     .WORD     DIB_6            ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE6        ";device name
          .BYTE     080              ;active
          .BYTE     001  ;0FF        ;slot number
          .BYTE     005              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB5_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     140A             ;Version 1.40A
          .WORD     0002             ;DCB length followed by DCB
          .BYTE     000              ;Drive # = $00 (master) or $01 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
          .BYTE     005              ;Partition number on the drive = $00-07
;
; Device identification Block (DIB) - Volume #6
;
DIB_6     .WORD     DIB_7            ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE7        ";device name
          .BYTE     080              ;active
          .BYTE     001  ;0FF        ;slot number
          .BYTE     006              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB6_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     140A             ;Version 1.40A
          .WORD     0002             ;DCB length followed by DCB
          .BYTE     000              ;Drive # = $00 (master) or $01 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
          .BYTE     006              ;Partition number on the drive = $00-07
;
; Device identification Block (DIB) - Volume #7
;
DIB_7     .WORD     0000             ;link pointer
          .WORD     Entry            ;entry pointer
          .BYTE     7                ;name length byte
          .ASCII    ".CFIDE8        ";device name
          .BYTE     080              ;active
          .BYTE     001  ;0FF        ;slot number
          .BYTE     007              ;unit number
          .BYTE     0D1              ;type
          .BYTE     010              ;subtype
          .BYTE     000              ;filler
DIB7_Blks .WORD     0000             ;# blocks in device
          .WORD     444A             ;manufacturer - DJ
          .WORD     140A             ;Version 1.40A
          .WORD     0002             ;DCB length followed by DCB
          .BYTE     000              ;Drive # = $00 (master) or $01 (slave)
                                     ;Upper 6 bits = Partition address.
                                     ;64 different partition addresses avail.
          .BYTE     007              ;Partition number on the drive = $00-07


;------------------------------------
;
; Local storage locations
;
;------------------------------------

SlotCX    .BYTE     000              ;compute C0X0 and store on init
PtBlkIdx  .BYTE     0A3,0A8,0B3,0B8,0C3,0C8,0D3,0D8  ;Offsets to 8 block
PtVolIdx  .BYTE     0A6,0AB,0B6,0BB,0C6,0CB,0D6,0DB  ;segment for ea partition
UnitStat  .BLOCK    008,0FF          ;if UnitStat,X = $FF then partition info
                                                ;not initialized
                                     ;if UnitStat,X > $0F then UnitStat = error code
                                     ;else UnitStat,X = partition number
                                     ;if UnitStat,0 = #XNODRIVE then driver is
                                                ;nonfunctional
LastOP    .BLOCK    008,0FF          ;last op for D_REPEAT calls
StBlk_HB  .BLOCK    008,000          ;Starting block number for SOS/ProDos
StBlk_MB  .BLOCK    008,000          ;vol #0, vol #1, vol #2, vol #3
StBlk_LB  .BLOCK    008,000          ;vol #4, vol #5, vol #6, vol #7
Block_LB  .BLOCK    008,000          ;Total blocks for each volume #
Block_HB  .BLOCK    008,000
DCB_Idx   .BYTE     000
          .BYTE     DIB1_Blks-DIB0_Blks
          .BYTE     DIB2_Blks-DIB0_Blks
          .BYTE     DIB3_Blks-DIB0_Blks
          .BYTE     DIB4_Blks-DIB0_Blks
          .BYTE     DIB5_Blks-DIB0_Blks
          .BYTE     DIB6_Blks-DIB0_Blks
          .BYTE     DIB7_Blks-DIB0_Blks
SIR_Addr  .WORD     SIR_Tbl
SIR_Tbl   .BLOCK    005,000
SIR_Len   .EQU      *-SIR_Tbl
PmapCall  .BLOCK    003,000          ;Block $hb mb lb for partition record
          .BYTE     001,000          ;Read 1 ATA sector (block) lb, hb
          .WORD     PmapBuf          ;Buffer address to place partition record
PmapBuf   .BLOCK    0100,000
Err_Data  .BLOCK    0100,000
RdBlk1Pr  .WORD     RdBlk1
WrBlk1Pr  .WORD     WrBlk1
RdBlk2Pr  .WORD     RdBlk2
WrBlk2Pr  .WORD     WrBlk2
RdBlk_Proc .WORD    0000
WrBlk_Proc .WORD    0000

;          .ASCII    "Written By Dale S. Jackson, initial writing 12/12/02"
;          .ASCII    "v1.40a revised 8/20/11 By Dave Schmenk"

;------------------------------------
;
; Driver request handlers
;
;------------------------------------

Entry     LDA       UnitStat+0
          CMP       #XNODEVIC
          BEQ       No_Drive
          LDX       SOS_Unit         ;get drive number for this unit
          LDY       DCB_Idx,X
          LDA       Driv_No0,Y
          STA       CurDrvNo
          AND       #001             ;only bit 0 counts
          STA       CurDrive         ;Set device to LBA mode
          ORA       #00E             ;device mode bits   %00001(LBA)1(Drive#)
          ASL       A                ;shift left 4 bits to high order nibble
          ASL       A
          ASL       A
          ASL       A
          STA       Drv_Parm
          LDA       ReqCode
          CMP       #002             ;Status Call
          BCS       $2
          LDA       UnitStat,X       ;Check if partition table is current
          CMP       #0FF
          BNE       $1
          JSR       GetPmap          ;fetch it if not.
          JSR       PInit
          LDX       SOS_Unit
          LDA       UnitStat,X
$1        CMP       #010             ;Check if SOS_Unit driver is good.
          BCS       Err_Out1
$2        JSR       Dispatch         ;Now call the dispatcher
          LDX       SOS_Unit         ;Save current operation
          LDA       ReqCode          ;for D_REPEAT processing
          STA       LastOP,X
          RTS
;
; The Dispatcher.  Does it depending on ReqCode.  Note
; that if we came in on a D_INIT call, we do a branch to
; Dispatch, normally.  Dispatch is called as a subroutine!
; We copy the buffer pointer and block # from the parameter
; area into our own temps, as the system seems to want them
; left ALONE.
;
DoTable   .WORD     DRead-1          ;0 read request
          .WORD     DWrite-1         ;1 write request
          .WORD     DStatus-1        ;2 status request
          .WORD     DControl-1       ;3 control request
          .WORD     BadReq-1         ;4 unused
          .WORD     BadReq-1         ;5 unused
          .WORD     BadOp-1          ;6 open - invalid request
          .WORD     BadOp-1          ;7 close - invalid request
          .WORD     DInit-1          ;8 init request
          .WORD     DRepeat-1        ;9 repeat request
Dispatch  SWITCH    ReqCode,9,DoTable ;go do it.
;
; Errors
;
BadReq    LDA       #XREQCODE        ;bad request code!
Err_Out1  JSR       SysErr           ;doesn't return
Slot_Err  LDA       #XNODEVIC        ;#XNORESRC        SIR not available
          STA       UnitStat+0       ;no! it didn't go ok.
No_Drive  LDA       #XNODRIVE        ;Flag this driver not useable
          JSR       SysErr           ;doesn't return
;
; D_INIT call processing for all Volumes.
; Called at system init time only.  Check DIB0_Slot to
; make sure that the user set a valid slot number for our
; interface.  Allocate it by calling AllocSIR. If slot not
; available then set UnitStat+0 to XNORESRC error code.
;
; Compute the system internal resource number (SIR) and
; call AllocSIR to try and grab that for us.  It performs
; slot checking as a side effect.
;
DInit     LDA       SIR_Tbl
          BNE       Norm_Out
          LDA       DIB0_Slot
          AND       #007
          ORA       #010             ;SIR = 16+slot#
          STA       SIR_Tbl
          LDA       #SIR_Len
          LDX       SIR_Addr
          LDY       SIR_Addr+1
          JSR       AllocSIR         ;this one's mine!
          BCS       Slot_Err
          LDA       DIB0_Slot        ;Compute C0X0 for this slot
          ASL       A
          ASL       A
          ASL       A
          ASL       A
          STA       SlotCX
          JSR       ResetIFC         ;Initialization of Device
          BCS       Slot_Err
          LDY       #007             ;Reset drive status to initial startup
          LDA       #0FF
$1        STA       UnitStat,Y
          DEY
          BPL       $1
Norm_Out  CLC
          RTS
;
; D_REPEAT - repeat the last D_READ or D_WRITE call
;
DRepeat   LDX       SOS_Unit
          LDA       LastOP,X         ;look at the last thing we did
          CMP       #002
          BCS       BadOp
          STA       ReqCode
          JMP       Dispatch
BadOp     LDA       #XBADOP          ;invalid operation!
          JSR       SysErr           ;doesn't return
;
; D_READ call processing
;
DRead     JSR       CkCnt
          LDA       #ATACRead
          STA       ATA_Cmd
CRead     LDA       #000             ;Zero # bytes read
          STA       Count
          STA       Count+1
          TAY
          STA       (QtyRead),Y      ;bytes read
          INY
          STA       (QtyRead),Y      ;msb of bytes read
          LDA       Num_Blks         ;check for Num_Blks greater than zero
          ORA       Num_Blks+1
          BEQ       ReadExit
$1        JSR       FixUp
          JSR       Read_Blk         ;Transfer a block to/from the disk
          LDY       #000
          LDA       Count
          STA       (QtyRead),y      ;Update # of bytes actually read
          INY
          LDA       Count+1
          STA       (QtyRead),y
          BCS       IO_Error         ;An error occurred
ReadExit  RTS                        ;exit read routines
SRead     JSR       Read_Blk         ;Transfer a block to/from the disk
          BCC       ReadExit
IO_Error  LDA       #XIOERROR        ;I/O error
          JSR       SysErr           ;doesn't return
;
; D_WRITE call processing
;
DWrite    JSR       CkCnt
          LDA       #ATACWrit
          STA       ATA_Cmd
CWrite    LDA       Num_Blks         ;check for Num_Blks greater than zero
          ORA       Num_Blks+1
          BEQ       WriteExit        ;quantity to write is zero
          JSR       FixUp
          JSR       Write_Blk
          BCS       IO_Error
WriteExit RTS
;
; D_STATUS call processing
;  $00   Drivers Status - always $0
;  $01   Return device identification - $0200 bytes long
;  $02   Return most recent device error information/data
;  $03   Return partition table data - $0100 bytes long
;  $04   Return DIB configuration bytes - 2 bytes long
;  $FE   Return preferrred bitmap location ($FFFF)
;
DStatus   LDA       CtlStat          ;status command
          BNE       $1
          TAY                        ;Driver Status = $0 always
          STA       (CSList),Y
          RTS
$1        CMP       #001
          BEQ       S_Ident
          CMP       #002
          BEQ       ErrStat
          CMP       #003
          BEQ       ParTable
          CMP       #004
          BEQ       DIBinfo
          CMP       #0FE
          BEQ       BitMap
CS_Bad    LDA       #XCTLCODE        ;control/status code no good
Err_Out3  JSR       SysErr           ;doesn't return
ParTable  JSR       GetPmap          ;Return partn table of current SOS_Unit
          BNE       $2               ;Partition map is not valid
          LDY       #000
$1        LDA       PmapBuf,Y
          STA       (CSList),Y
          INY
          BNE       $1
          RTS
$2        JMP       No_Drive
ErrStat   LDY       #005            ;Return most recent error data.
$1        LDA       Err_Data,y      ; Byte 0:      Device Status Code
          STA       (CSList),Y      ; Byte 1:      Device Error Code 
          DEY                       ; (if status ERR bit is 0, error code = 0)
          BPL       $1              ; Byte 2,3,4:  Sector# (LB,MB,HB) of error
          CLC                       ; Byte 5:      # of sectors left to xfer
          RTS
DIBinfo   LDX       SOS_Unit         ;Return DIB configuration bytes
          LDY       DCB_Idx,X
          LDA       Part_No0,Y       ;Get assigned partition number this driver
          PHA
          LDA       Driv_No0,Y       ;Get assigned partition map/IDE device
          LDY       #000             ;for this driver.
          STA       @CSList,Y
          INY
          PLA
          STA       @CSList,Y
          CLC
          RTS
BitMap    LDY       #000             ;Return preferred bit map locations.
          LDA       #0FF             ;We return FFFF, don't care
          STA       (CSList),Y
          INY
          STA       (CSList),Y       
          CLC
          RTS
S_Ident   LDA       CSList            ;Device Identification
          STA       DataBuf
          LDA       CSList+1
          STA       DataBuf+1
          LDA       CSList+ExtPG
          STA       DataBuf+ExtPG
          LDA       #ATAIdent
          STA       ATA_Cmd
C_Ident   LDA       #001
          STA       Num_Blks
          LDA       #000
          STA       Num_Blks+1
          JMP       SRead

;
; D_CONTROL call processing
;  $00     Reset device
;  $01     Perform device I/O function with user supplied
;          call block.
;  $04     Set DIB configuration bytes
;  $FE     Perform media formatting
;
DControl  LDA       CtlStat          ;control command
          BEQ       CReset
          CMP       #001
          BEQ       UserIO
          CMP       #004
          BEQ       New_DIB
          CMP       #0FE             ;formatting?
          BEQ       MFormat
          JMP       CS_Bad           ;Control code no good!
CReset    JSR       ResetIFC         ;Reset CFFA card
          BCS       $1
          RTS
$1        LDA       #XNORESET
          JSR       SysErr           ;doesn't return
MFormat   LDX       #007             ;Execute media formatting call.
$1        LDY       DCB_Idx,X
          LDA       Driv_No0,Y
          CMP       CurDrvNo
          BNE       $2
          LDA       #0FF             ;Invalidate partition table status
          STA       UnitStat,X       ;so subsequent read/writes
$2        DEX                        ;will re-initialize the partition info
          BPL       $1               ;for each driver designated for this
          CLC                        ;partiton table.
          RTS
New_DIB   LDX       SOS_Unit         ;Save new DIB configuration bytes
          LDA       #0FF             ;Invalidate partition table status of driver
          STA       UnitStat,X
          LDY       #000
          LDA       @CSList,Y
          PHA
          INY
          LDA       @CSList,Y
          LDY       DCB_Idx,X
          STA       Part_No0,Y      ;Get assigned partition number this driver
          PLA
          STA       Driv_No0,Y      ;Get assigned partition map/IDE device
          CLC                       ;for this driver.
          RTS
UserIO    LDY       #004
$1        LDA       (CSList),Y
          STA       ATA_Cmd+1,Y
          DEY
          BNE       $1
          LDA       Num_Blks         ;if zero then 256 blocks is requested
          BNE       $2
          INY
$2        STY       Num_Blks+1
          CLC                        ;Setup data addresses
          LDA       CSList
          ADC       #005
          STA       QtyRead
          LDA       CSList+1
          ADC       #000
          STA       QtyRead+1
          LDA       QtyRead
          ADC       #002
          STA       DataBuf
          LDA       QtyRead+1
          ADC       #000
          STA       DataBuf+1
          LDA       CSList+ExtPG
          STA       QtyRead+ExtPG
          STA       DataBuf+ExtPG
          LDY       #000
          LDA       (CSList),Y
          LDY       #006
$3        CMP       Ctrl_Cmds,y
          BEQ       $4
          DEY
          BPL       $3
          JMP       CS_Bad
$4        STA       ATA_Cmd
          TYA
          ASL       A
          TAY
          LDA       Cmd_Tbl+1,Y
          PHA
          LDA       Cmd_Tbl,Y
          PHA
          RTS
;
; Perform I/O with user supplied call block
; Call Block Organization:
;     Byte 0:        ATA Command Code
;     Byte 1,2,3:    Sector# (HB,MB,LB absolute sector)
;     Byte 4:        # of sectors
;     Byte 5-6:      Bytes returned to buffer
;     Byte 7...      Data Buffer
;
CtrlCmds  .BYTE     ATA_Xerr
          .BYTE     ATACRead
          .BYTE     ATACWrit
          .BYTE     ATA_Vrfy
          .BYTE     ATA_Frmt
          .BYTE     ATA_Diag
          .BYTE     ATAIdent

Cmd_Tbl   .WORD     Send_Cmd-1       ;Extended Error Info  $03
          .WORD     CRead-1          ;Sector Read  $20
          .WORD     CWrite-1         ;Sector Write  $30
          .WORD     Verify-1         ;Read verify  $40
          .WORD     CWrite-1         ;Sector Format  $50
          .WORD     Send_Cmd-1       ;Internal Diagnostic Test  $90
          .WORD     C_Ident-1        ;Device Identity  $EC

Send_Cmd  JSR       CkDevice
          LDA       #000
          STA       ATdataHB,x       ;Clear high byte data latch
          LDA       ATA_Cmd
          STA       ATCmdReg,x       ;Issue the ATA command to the drive
$1        LDA       ATA_Stat,x
          BMI       $1
          LDA       ATAError,x
          LDY       #000
          STA       (DataBuf),y
          JMP       CSet2Mhz
;
; Verify - Verify requested blocks
;
Verify    LDA       #ATA_Vrfy
          JSR       SetupLBA         ;Program the device's task file registers
$1        LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       $1
          LSR       A
          BCS       $2
          JMP       Set2Mhz
$2        JMP       Save_Err

;------------------------------------
;
; Partition Table routines
;
;------------------------------------

;
; Get Partition Map from drive
;
GetPmap   LDA       CurDrvNo
          AND       #0FC             ;get upper 6 bits of drive number for
          STA       PmapCall         ;partition address.
          LDY       #006
$1        LDA       PmapCall,Y
          STA       Sect_HB,Y
          DEY
          BPL       $1
          LDA       #000
          STA       DataBuf+ExtPG
          LDA       #ATACRead
          STA       ATA_Cmd
          JSR       SRead
          LDY       #002             ;Compute partition table checksum
          LDA       #0A5             ;Carry is initially clear
$2        EOR       PmapBuf,Y
          INY
          ADC       PmapBuf,Y
          INY
          BNE       $2
          EOR       PmapBuf,Y
          STA       Validate
          RTS                        ;Return Validate=$0 if valid partition
;
; Initialize partition info for each partition in the current table.
;
PInit     LDX       #007
Next_DIB  LDY       DCB_Idx,X
          LDA       CurDrvNo
          CMP       Driv_No0,Y
          BNE       IncDIB_x
          LDA       Validate
          BNE       Bad_Drv
          LDA       Part_No0,Y       ;Get assigned partition number for this
          CMP       #008             ;driver.
          BCS       Bad_Drv          ;Partition number is out of range
          STA       CurPart
          JSR       GVolParm
          BCS       Bad_Drv
          LDY       DCB_Idx,X
          LDA       Block_HB,X
          STA       DIB0_Blks+1,Y
          LDA       Block_LB,X
          STA       DIB0_Blks,Y
          LDA       CurPart
          BPL       Good_Drv
Bad_Drv   LDA       #XNODRIVE        ;Flag this driver not useable
Good_Drv  STA       UnitStat,X
IncDIB_x  DEX
          BPL       Next_DIB
          RTS
;
; Get the volume start block in this partition segment
;
GVolParm  LDY       CurPart          ;Xreg contains DIB unit number
          LDA       PtBlkIdx,Y
          TAY
          LDA       PmapBuf+2,Y      ;Block HB
          CMP       #004             ;Test if beginning track is valid
          BCS       $1               ;Nope!  Beginning block larger than 16 mb
          ORA       PmapCall         ;set upper 6 bits for partition address
          STA       StBlk_HB,X
          LDA       PmapBuf+1,Y      ;Block MB
          STA       StBlk_MB,X
          ORA       PmapBuf+2,Y      ;Check if beginning track is zero
          ORA       PmapBuf,Y
          BEQ       Bad_Vol          ;Volume start block can't be zero
          LDA       PmapBuf,Y        ;Block LB
          STA       StBlk_LB,X
          LDY       CurPart          ;Xreg contains DIB unit number
          LDA       PtVolIdx,Y       ;Get the volume size
          TAY
          LDA       PmapBuf,Y
          STA       Block_LB,X
          ORA       PmapBuf+1,Y
          BEQ       Bad_Vol          ;Volume size cannot be zero
          LDA       PmapBuf+1,Y
          STA       Block_HB,X
          BMI       Bad_Vol          ;Volume size is greater than 32767 blocks
          CLC
$1        RTS
Bad_Vol   SEC
          RTS

;------------------------------------
;
; Utility routines
;
;------------------------------------

;
; Check ReqCnt to insure it's a multiple of 512.
;
CkCnt     LDA       ReqCnt           ;look at the lsb of bytes to do
          BNE       $1               ;no good!  lsb should be 00
          STA       Num_Blks+1       ;zero high byte of number of blocks
          LDA       ReqCnt+1         ;look at the msb
          LSR       A                ;put bottom bit into carry, 0 into top
          STA       Num_Blks         ;save as number of blocks to transfer
          BCC       CvtBlk           ;Carry is set from LSR to mark error.
$1        LDA       #XBYTECNT
          JSR       SysErr           ;doesn't return
;
; Test for valid block number. Carry clear on return means
; no error.  Carry set means block number bad.  X register
; contains volume number.
;
CvtBlk    LDA       SosBuf
          STA       DataBuf
          LDA       SosBuf+1
          STA       DataBuf+1
          LDA       SosBuf+ExtPG
          STA       DataBuf+ExtPG
          LDY       SOS_Unit
          LDA       SosBlk
          CMP       Block_LB,y
          LDA       SosBlk+1
          SBC       Block_HB,y
          BCS       BlkErr
          LDA       SosBlk
          ADC       StBlk_LB,y
          STA       Sect_LB
          LDA       SosBlk+1
          ADC       StBlk_MB,y
          STA       Sect_MB
          LDA       StBlk_HB,y
          ADC       #000
          STA       Sect_HB
          RTS
BlkErr    LDA       #XBLKNUM
          JSR       SysErr             ;doesn't return
IncrAdr   INC       Count+1
          INC       Count+1
BumpAdr   INC       DataBuf+1         ;increment DataBuf msb
;
; Fix up the buffer pointer to correct for an addressing
; anomalies!  We just need to do the initial checking
; for two cases:
; 00xx bank N -> 80xx bank N-1
; 20xx bank 8F if N was 0
; FDxx bank N -> 7Dxx bank N+1
; If pointer is adjusted, return with carry set
;
FixUp     LDA       DataBuf+1         ;look at msb
          BEQ       $1                 ;that's one!
          CMP       #0FD               ;is it the other one?
          BCS       $2                 ;yep. fix it!
          RTS                          ;Pointer unchanged, return carry clear.
$1        LDA       #080               ;00xx -> 80xx
          STA       DataBuf+1
          DEC       DataBuf+ExtPG     ;bank N -> band N-1
          LDA       DataBuf+ExtPG     ;see if it was bank 0
          CMP       #07F               ;(80) before the DEC.
          BNE       $3                 ;nope! all fixed.
          LDA       #020               ;if it was, change both
          STA       DataBuf+1         ;msb of address and
          LDA       #08F
          STA       DataBuf+ExtPG     ;bank number for bank 8F
          RTS                          ;return carry set
$2        AND       #07F               ;strip off high bit
          STA       DataBuf+1         ;FDxx ->7Dxx
          INC       DataBuf+ExtPG     ;bank N -> bank N+1
$3        RTS                          ;return carry set
;
; Wait - Copy of Apple's wait routine.
;  Input:
;   A =  delay time, where Delay(us) = 2.5A^2 + 13.5A + 36
;        including JSR to this routine.
;        or more typically A = (Delay[in uS]/2.5 - 7.11)^.5 - 2.7
Wait      PHP
          SEI
          SEC
$1        PHA
$2        SBC       #001
          BNE       $2
          PLA
          SBC       #001
          BNE       $1
          PLP
          RTS
;
; Throttle back to 1 MHz
;
Set1Mhz   PHP
          SEI
          LDA       EReg
          ORA       #080
          STA       EReg
          PLP
          RTS
;
; Throttle up to 2 MHz
;
CSet2Mhz  CLC
Set2Mhz   PHP
          SEI
          LDA       EReg
          AND       #07F
          STA       EReg
          PLP
          RTS

;----------------------------------
;
; Supplemental Device Subroutines
;
; Device Internal Diagnostic Routine  ATA Command $90
;  Returns 1 byte of diagnostic code in Buffer
;  $01 = No Error Detected
;  $02 = Formatter Device Error
;  $03 = Sector Buffer Error
;  $04 = ECC Circuitry Error
;  $05 = Controlling Microprocessor Error
;  $8x = Slave Failed (true IDE mode)
;
; Extended Error Code Request
;  Returns 1 byte of exteded error code in Buffer
;  $00 = No Error Detected
;  $01 = Self test OK (No error)
;  $09 = Miscellaneous Error
;  $20 = Invalid Command
;  $21 = Invalid address (requested head or sector invalid)
;  $2F = Address Overflow (address too large)
;  $35,$36 = Supply voltage out of tolerance
;  $11 = Uncorrectable ECC error
;  $18 = Corrected ECC Error
;  $05,$30-34,$37,$3E = Self test or diagnostic failed
;  $10,$14 = ID not found
;  $3A = Spare sectors exhausted
;  $1F = Data transfer error/Aborted command
;  $0C,$38,$3B,$3C,$3F = Corrupted Media Format
;  $03 = Write/Erase failed
;
;----------------------------------

;
; Execute reset call to ATA device
;
ResetIFC  JSR       Set1Mhz
          LDA       RdBlk1Pr
          STA       RdBlk_Proc
          LDA       RdBlk1Pr+1
          STA       RdBlk_Proc+1
          LDA       WrBlk1Pr
          STA       WrBlk_Proc
          LDA       WrBlk1Pr+1
          STA       WrBlk_Proc+1
;          LDA       DIB0_Slot
;          JSR       SELC800         ;Turn on ROM
;          LDA       IFC_ID+2
;          AND       #0F0            ;Check major ROM version is 2.xx
;          EOR       IFC_ID+1
;          EOR       IFC_ID
;          TAX
;          LDA       #000
;          JSR       SELC800         ;Turn off ROM
;          CPX       #015
;          BNE       No_Ver2
          LDA       Fast_Xfer
          BEQ       No_Ver2
          LDA       RdBlk2Pr
          STA       RdBlk_Proc
          LDA       RdBlk2Pr+1
          STA       RdBlk_Proc+1
          LDA       WrBlk2Pr
          STA       WrBlk_Proc
          LDA       WrBlk2Pr+1
          STA       WrBlk_Proc+1
No_Ver2   LDX       SlotCX
          LDA       ClrCSMsk,x      ;reset MASK bit in PLD for normal CS0
          LDA       #000            ;signaling.
          STA       ATdataHB,x      ;Clear high byte data latch
          LDA       #006            ;Reset bit=1, Disable INTRQ=1
          STA       ATAdCtrl,x
          JSR       Norm_Out        ;Per ATA-6 spec, need to wait 5us minimum.
          LDA       #002            ;Reset bit=0, Disable INTRQ=1
          STA       ATAdCtrl,x
          LDY       #208.           ;Per ATA-6 spec, need to wait 2ms minimum
          LDA       #Wait5ms        ;Use a initial delay of 5ms.
$1        JSR       Wait
          LDA       ATA_Stat,x      ;Per ATA-6 spec, wait up to 31 sec for
          BMI       $2              ;busy to clear if a slave is attached.
          JMP       CSet2Mhz
$2        LDA       #Wait150ms      ;Wait for up to 31 secs if a slave is
          DEY                       ;attached.  After 31 secs pass, the card
          BNE       $1              ;is not installed in slot.
          SEC                       ;Drive(s) Not Ready
          JMP       Set2Mhz
;
; Check Device - test drive status register is readable
; and equal to $40
;
CkDevice  JSR       Set1Mhz
          LDY       #200.
          LDX       SlotCX
          LDA       ClrCSMsk,x       ;reset MASK bit in PLD for normal CS0
$1        LDA       ATA_Stat,x       ;signaling.
          BMI       $1
          AND       #008             ;%00001000  Check bus can receive a
          BEQ       $2               ;device register setting DRQ=0
          LDA       #Wait5ms
          JSR       Wait             ;Wait 5ms to try again
          DEY
          BNE       $1              ;Wait up to 1 second for drive to be ready
          BEQ       NotReady        ;always taken
$2        LDA       Drv_Parm        ;Select drive to check
          STA       ATAHead,x
          LDY       #200.
$3        LDA       ATA_Stat,x
          BMI       $3
          AND       #0E9             ;%11101001  Check if device is ready to
          CMP       #040             ;receive a command.  If BSY=0, RDY=1,
          BNE       $4               ;DF=0, DRQ=0, and ERR=0
          RTS                        ;We're good to go.  Returns @ 1 Mhz clock
                                     ;Xreg = SlotCX, and carry set.
$4        LDA       #Wait5ms
          JSR       Wait             ;Wait 5ms to try again
          DEY
          BNE       $3               ;Wait up to 1 seconds for drive readiness
NotReady  JSR       Set2Mhz
          LDA       #XCKDEVER        ;DEVICE NOT READY error
          JSR       SysErr
;
; SetupLBA - Programs devices task registers with LBA data
; Input:
;        partition data Sect_HB, MB, & LB
;        A =  command
;        X =  requested slot number in form $n0 where
;             n =  slot 1 to 7
; This function programs the device registers with
; the ATA Logical Block Address (LBA) to be accessed.
; A SOS block and a ATA sector are both 512 bytes.
; Logical Block Mode, the Logical Block Address is
; interpreted as follows:
;  LBA07-LBA00: Sector Number Register D7-D0.
;  LBA15-LBA08: Cylinder Low Register D7-D0.
;  LBA23-LBA16: Cylinder High Register D7-D0.
;  LBA27-LBA24: Drive/Head Register bits HS3-HS0.
;
SetupLBA  PHA
          JSR       CkDevice         ;returns with carry set
          LDA       Sect_LB
          STA       ATSector,x       ;store low block # into LBA 0-7
          LDA       Sect_MB
          STA       ATSector+1,x     ;store mean block # into LBA 15-8
          LDA       Sect_HB
          STA       ATSector+2,x     ;store high block # LBA bits 23-16
          LDA       Num_Blks
          STA       ATSectCt,x       ;store # of blocks to be read/written
          LDA       #000
          STA       ATdataHB,x
          TAY
          PLA
          STA       ATCmdReg,x       ;Issue the command
          RTS
;
; Save device error status
;
Save_Err  LDA       ATA_Stat,x
          STA       Err_Data
          LSR       A
          BCS       $1               ;if status ERR bit is one then get error
          LDA       #000             ;register data,else return error data
          BEQ       $2               ;byte = zero.
$1        LDA       ATAError,x
$2        STA       Err_Data+1
          LDA       ATSector,x       ;retrieve sector # error occurred
          STA       Err_Data+2       ;LB
          LDA       ATSector+1,x
          STA       Err_Data+3       ;MB
          LDA       ATSector+2,x
          STA       Err_Data+4       ;HB
          LDA       ATSectCt,x
          STA       Err_Data+5       ;retrieve # of sectors left
          STY       Count
          SEC
          JMP       Set2Mhz
;
; Read_Blk - Read requested blocks from device into memory
;
;
Read_Blk  LDA       ATA_Cmd
          JSR       SetupLBA         ;Program the device's task file registers
          JMP       @RdBlk_Proc
;
; CFFA Ver 1.x
;
RdBlk1    LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       RdBlk1
          AND       #009             ;Check for error response from device
          CMP       #008             ;If DRQ=0 or ERR=1 a device error
          BNE       Read_Err
$1        LDA       ATdataLB,x
          STA       (DataBuf),y
          INY
          LDA       ATdataHB,x
          STA       (DataBuf),y
$2        LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       $2
          INY
          BNE       $1
          INC       DataBuf+1
$3        LDA       ATdataLB,x
          STA       (DataBuf),y
          INY
          LDA       ATdataHB,x
          STA       (DataBuf),y
$4        LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       $4
          INY
          BNE       $3
          JSR       IncrAdr
          DEC       Num_Blks         ;did we get what was asked for
          BNE       RdBlk1
          DEC       Num_Blks+1
          BPL       RdBlk1
          JMP       CSet2Mhz
Read_Err  JMP       Save_Err
;
; CFFA Ver 2.x
;
RdBlk2    LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       RdBlk2
          AND       #009             ;Check for error response from device
          CMP       #008             ;If DRQ=0 or ERR=1 a device error
          BNE       Read_Err
$1        LDA       ATdataLB,x
          STA       (DataBuf),y
          INY
          LDA       ATdataHB,x
          STA       (DataBuf),y
          INY
          BNE       $1
          INC       DataBuf+1
$2        LDA       ATdataLB,x
          STA       (DataBuf),y
          INY
          LDA       ATdataHB,x
          STA       (DataBuf),y
          INY
          BNE       $2
          JSR       IncrAdr
          DEC       Num_Blks         ;did we get what was asked for
          BNE       RdBlk2
          DEC       Num_Blks+1
          BPL       RdBlk2
          JMP       CSet2Mhz
;
; Write_Blk - write requested blocks to device from memory
;
Write_Blk LDA       ATA_Cmd
          JSR       SetupLBA         ;Program the device's task file registers
          JMP       @WrBlk_Proc
;
; CFFA Ver 1.x
;
WrBlk1    LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       WrBlk1
          AND       #009             ;Check for error response from device
          CMP       #001             ;If DRQ=0 and ERR=1 a device error
          BEQ       Write_Err
$1        LDA       SetCSMsk,x       ;any access sets mask bit to block
                                     ;IDE -CS0 on I/O read to drive
          INY
          LDA       (DataBuf),y
          STA       ATdataHB,x
          DEY
          LDA       (DataBuf),y
          STA       ATdataLB,x
          LDA       ClrCSMsk,x
$2        LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       $2
          INY
          INY
          BNE       $1
          INC       DataBuf+1
$3        LDA       SetCSMsk,x
          INY
          LDA       (DataBuf),y
          STA       ATdataHB,x
          DEY
          LDA       (DataBuf),y
          STA       ATdataLB,x
          LDA       ClrCSMsk,x       ;Set back to normal, allow CS0 assertions
                                     ;on read cycles
$4        LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       $4
          INY
          INY
          BNE       $3
          JSR       BumpAdr
          DEC       Num_Blks         ;did we do what was asked for
          BNE       WrBlk1
          DEC       Num_Blks+1       ;we might have to do this one more time
          BPL       WrBlk1
          LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          AND       #009             ;Check for error response from device
          CMP       #001             ;If DF=1 or DRQ=0 or ERR=1 a device error
          BEQ       Write_Err
          JMP       CSet2Mhz         ;exit write routines
Write_Err JMP       Save_Err
;
; CFFA Ver 2.x
;
WrBlk2    LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       WrBlk2
          AND       #009             ;Check for error response from device
          CMP       #001             ;If DRQ=0 or ERR=1 a device error
          BEQ       Write_Err
          LDA       SetCSMsk,x       ;any access sets mask bit to block
                                     ;IDE -CS0 on I/O read to drive
$1        INY
          LDA       (DataBuf),y
          STA       ATdataHB,x
          DEY
          LDA       (DataBuf),y
          STA       ATdataLB,x
          INY
          INY
          BNE       $1
          INC       DataBuf+1
$2        INY
          LDA       (DataBuf),y
          STA       ATdataHB,x
          DEY
          LDA       (DataBuf),y
          STA       ATdataLB,x
          INY
          INY
          BNE       $2
          LDA       ClrCSMsk,x       ;Set back to normal, allow CS0 assertions
                                     ;on read cycles
          JSR       BumpAdr
          DEC       Num_Blks         ;did we do what was asked for
          BNE       WrBlk2
          DEC       Num_Blks+1       ;we might have to do this one more time
          BPL       WrBlk2
$3        LDA       ATA_Stat,x       ;Wait for BUSY flag to clear
          BMI       $3
          AND       #009             ;Check for error response from device
          CMP       #001             ;If DF=1 or DRQ=0 or ERR=1 a device error
          BEQ       Write_Err
          JMP       CSet2Mhz         ;exit write routines

         .END
