a3DriverDepot/drivers/cfideiii/CFIDE.s
2021-07-04 09:58:17 -04:00

1328 lines
51 KiB
ArmAsm

;CF140A
; .TITLE "Apple /// CompactFlash/IDE Driver Ver 1.40A"
;Limit Maximum Volume Size to 32767 Blocks
; .PROC CFIDE
;
; SOS Equates
;
ExtPG = $1401 ;driver extended bank address offset
AllocSIR = $1913 ;allocate system internal resource
SELC800 = $1922 ;Enable Expansion Rom Space
DeAlcSIR = $1916 ;de-allocate system internal resource
SysErr = $1928 ;report error to system
EReg = $FFDF ;environment register
ReqCode = $C0 ;request code
SOS_Unit = $C1 ;unit number
SosBuf = $C2 ;SOS buffer pointer
ReqCnt = $C4 ;requested byte count
CtlStat = $C2 ;control/status code
CSList = $C3 ;control/status list pointer
SosBlk = $C6 ;starting block number
QtyRead = $C8 ;bytes read return by D_READ
;
; Our temps in zero page
;
CurPart = $CC ;1 byte
Count = $CD ;2 bytes
Validate = $CE ;1 byte
;
; Parameter block specific to current SOS request
;
ATA_Cmd = $CF
Drv_Parm = $D0
Sect_HB = $D1
Sect_MB = $D2
Sect_LB = $D3
Num_Blks = $D4 ;2 bytes lb,hb
DataBuf = $D6 ;2 bytes
CurDrive = $D8 ;Current IDE drive number of SOS_Unit #
CurDrvNo = $DA ;Current DIB drive number of SOS_Unit #
;
; SOS Error Codes
;
XREQCODE = $20 ;Invalid request code
XCTLCODE = $21 ;Invalid control/status code
XCTLPARAM = $22 ;Invalid control/status parameter
XNORESRC = $25 ;Resource not available
XBADOP = $26 ;Invalid operation
XIOERROR = $27 ;I/O error
XNODRIVE = $28 ;Drive not connected
XBYTECNT = $2C ;Byte count not a multiple of 512
XBLKNUM = $2D ;Block number to large
XDCMDERR = $31 ;device command ABORTED error occurred
XCKDEVER = $32 ;Check device readiness routine failed
XNORESET = $33 ;Device reset failed
XNODEVIC = $38 ;Device not connected
;
; IFC I/O locations
;
IFC_ID = $C820
IOBase = $C080
ATdataHB = IOBase+$00
SetCSMsk = IOBase+$01 ;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 = IOBase+$02 ;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 = IOBase+$06 ;when reading (not used)
ATAdCtrl = IOBase+$06 ;when writing
ATdataLB = IOBase+$08
ATAError = IOBase+$09
Features = IOBase+$09
ATSectCt = IOBase+$0a
ATSector = IOBase+$0b ;11=LB,12=MB,13=HB
ATAHead = IOBase+$0e
ATCmdReg = IOBase+$0f ;when writing
ATA_Stat = IOBase+$0f ;when reading
;
; ATA/CF Commands Codes
;
ATA_XErr = $03
ATACRead = $20
ATACWrit = $30
ATA_Vrfy = $40
ATA_Frmt = $50
ATA_Diag = $90
ATAIdent = $EC
SetFeatr = $EF
;
; Constants for Wait
;
Wait5ms = $2a ; 42.
Wait150ms = $f2 ; 242.
;
; Switch Macro
;
.MACRO SWITCH index,bounds,adrs_table,noexec
.IFNBLANK index ; If PARM1 is present,
lda index ; load A with switch index
.ENDIF
.IFNBLANK bounds ; If PARM2 is present,
cmp #bounds+1 ; perform bounds checking
bcs @110 ; on switch index
.ENDIF
asl A ; Multiply by 2 for table index
tay
lda adrs_table+1,y ; Get switch address from table
pha ; and push onto Stack
lda adrs_table,y
pha
.IFBLANK noexec
rts ; Exit to code
.ENDIF
@110:
.ENDMACRO
.SEGMENT "TEXT"
;
; Comment Field of driver
;
.WORD $FFFF
.WORD $53
.byte "Apple /// CFFA Driver - "
.byte "written by Dale S. Jackson 8/08"
.byte ", modified by D Schmenk 8/11"
.SEGMENT "DATA"
;
; 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
.byte ".CFIDE1 ";device name
; .BYTE 0C0 ;active, page aligned
.BYTE $80 ;active, no page alignment
DIB0_Slot:
.BYTE $FF ;slot number
.BYTE $00 ;unit number
.BYTE $D1 ;type
.BYTE $10 ;subtype
.BYTE $00 ;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 $00 ;Drive #, Bit 0=$0 (master) or $1 (slave)
;Upper 6 bits = Partition address.
;64 different partition addresses avail.
Part_No0: .BYTE $00 ;Partition number on the drive = $00-07
FastXfer: .BYTE $00 ;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
.byte ".CFIDE2 ";device name
.BYTE $80 ;active
.BYTE $FF ;slot number
.BYTE $01 ;unit number
.BYTE $D1 ;type
.BYTE $10 ;subtype
.BYTE $00 ;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 $00 ;Drive # = $00 (master) or $01 (slave)
;Upper 6 bits = Partition address.
;64 different partition addresses avail.
.BYTE $01 ;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
.byte ".CFIDE3 ";device name
.BYTE $80 ;active
.BYTE $FF ;slot number
.BYTE 002 ;unit number
.BYTE $D1 ;type
.BYTE $10 ;subtype
.BYTE $00 ;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 $00 ;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
.byte ".CFIDE4 ";device name
.BYTE $80 ;active
.BYTE $FF ;slot number
.BYTE 003 ;unit number
.BYTE $D1 ;type
.BYTE $10 ;subtype
.BYTE $00 ;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 $00 ;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
.byte ".CFIDE5 ";device name
.BYTE $80 ;active
.BYTE $FF ;slot number
.BYTE 004 ;unit number
.BYTE $D1 ;type
.BYTE $10 ;subtype
.BYTE $00 ;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 $00 ;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
.byte ".CFIDE6 ";device name
.BYTE $80 ;active
.BYTE $FF ;slot number
.BYTE 005 ;unit number
.BYTE $D1 ;type
.BYTE $10 ;subtype
.BYTE $00 ;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 $00 ;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
.byte ".CFIDE7 ";device name
.BYTE $80 ;active
.BYTE $FF ;slot number
.BYTE $06 ;unit number
.BYTE $D1 ;type
.BYTE $10 ;subtype
.BYTE $00 ;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 $00 ;Drive # = $00 (master) or $01 (slave)
;Upper 6 bits = Partition address.
;64 different partition addresses avail.
.BYTE $06 ;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
.byte ".CFIDE8 ";device name
.BYTE $80 ;active
.BYTE $FF ;slot number
.BYTE $07 ;unit number
.BYTE $D1 ;type
.BYTE $10 ;subtype
.BYTE $00 ;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 $00 ;Drive # = $00 (master) or $01 (slave)
;Upper 6 bits = Partition address.
;64 different partition addresses avail.
.BYTE $07 ;Partition number on the drive = $00-07
;------------------------------------
;
; Local storage locations
;
;------------------------------------
SlotCX: .BYTE $00 ;compute C0X0 and store on init
PtBlkIdx: .BYTE $A3,$A8,$B3,$B8,$C3,$C8,$D3,$D8 ;Offsets to 8 block
PtVolIdx: .BYTE $A6,$AB,$B6,$BB,$C6,$CB,$D6,$DB ;segment for ea partition
UnitStat: .RES $08,$FF ;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: .RES $08,$FF ;last op for D_REPEAT calls
StBlk_HB: .RES $08,$00 ;Starting block number for SOS/ProDos
StBlk_MB: .RES $08,$00 ;vol #0, vol #1, vol #2, vol #3
StBlk_LB: .RES $08,$00 ;vol #4, vol #5, vol #6, vol #7
Block_LB: .RES $08,$00 ;Total blocks for each volume #
Block_HB: .RES $08,$00
DCB_Idx: .BYTE $00
.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: .RES $05,$00
SIR_Len = *-SIR_Tbl
PmapCall: .RES $03,$00 ;Block $hb mb lb for partition record
.BYTE $01,$00 ;Read 1 ATA sector (block) lb, hb
.WORD PmapBuf ;Buffer address to place partition record
PmapBuf: .RES $0100,$00
Err_Data: .RES $0100,$00
RdBlk1Pr: .WORD RdBlk1
WrBlk1Pr: .WORD WrBlk1
RdBlk2Pr: .WORD RdBlk2
WrBlk2Pr: .WORD WrBlk2
RdBlk_Proc:
.WORD $0000
WrBlk_Proc:
.WORD $0000
; .byte "Written By Dale S. Jackson, initial writing 12/12/02"
; .byte "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 #$01 ;only bit 0 counts
STA CurDrive ;Set device to LBA mode
ORA #$0E ;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 #$02 ;Status Call
BCS @2
LDA UnitStat,X ;Check if partition table is current
CMP #$FF
BNE @1
JSR GetPmap ;fetch it if not.
JSR PInit
LDX SOS_Unit
LDA UnitStat,X
@1: CMP #$10 ;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 #$07
ORA #$10 ;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 #$07 ;Reset drive status to initial startup
LDA #$FF
@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 #$02
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 #$00 ;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 #$00
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 #$01
BEQ S_Ident
CMP #$02
BEQ ErrStat
CMP #$03
BEQ ParTable
CMP #$04
BEQ DIBinfo
CMP #$FE
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 #$00
@1: LDA PmapBuf,Y
STA (CSList),Y
INY
BNE @1
RTS
@2: JMP No_Drive
ErrStat: LDY #$05 ;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 #$00 ;for this driver.
STA (CSList),Y
INY
PLA
STA (CSList),Y
CLC
RTS
BitMap: LDY #$00 ;Return preferred bit map locations.
LDA #$FF ;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 #$01
STA Num_Blks
LDA #$00
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 #$01
BEQ UserIO
CMP #$04
BEQ New_DIB
CMP #$FE ;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 #$07 ;Execute media formatting call.
@1: LDY DCB_Idx,X
LDA Driv_No0,Y
CMP CurDrvNo
BNE @2
LDA #$FF ;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 #$FF ;Invalidate partition table status of driver
STA UnitStat,X
LDY #$00
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 #$04
@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 #$05
STA QtyRead
LDA CSList+1
ADC #$00
STA QtyRead+1
LDA QtyRead
ADC #$02
STA DataBuf
LDA QtyRead+1
ADC #$00
STA DataBuf+1
LDA CSList+ExtPG
STA QtyRead+ExtPG
STA DataBuf+ExtPG
LDY #$00
LDA (CSList),Y
LDY #$06
@3: CMP CtrlCmds,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 #$00
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 #$00
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 #$FC ;get upper 6 bits of drive number for
STA PmapCall ;partition address.
LDY #$06
@1: LDA PmapCall,Y
STA Sect_HB,Y
DEY
BPL @1
LDA #$00
STA DataBuf+ExtPG
LDA #ATACRead
STA ATA_Cmd
JSR SRead
LDY #$02 ;Compute partition table checksum
LDA #$A5 ;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 #$07
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 #$08 ;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 #$04 ;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 #$00
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 #$FD ;is it the other one?
BCS @2 ;yep. fix it!
RTS ;Pointer unchanged, return carry clear.
@1: LDA #$80 ;00xx -> 80xx
STA DataBuf+1
DEC DataBuf+ExtPG ;bank N -> band N-1
LDA DataBuf+ExtPG ;see if it was bank 0
CMP #$7F ;(80) before the DEC.
BNE @3 ;nope! all fixed.
LDA #$20 ;if it was, change both
STA DataBuf+1 ;msb of address and
LDA #$8F
STA DataBuf+ExtPG ;bank number for bank 8F
RTS ;return carry set
@2: AND #$7F ;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 #$01
BNE @2
PLA
SBC #$01
BNE @1
PLP
RTS
;
; Throttle back to 1 MHz
;
Set1Mhz: PHP
SEI
LDA EReg
ORA #$80
STA EReg
PLP
RTS
;
; Throttle up to 2 MHz
;
CSet2Mhz: CLC
Set2Mhz: PHP
SEI
LDA EReg
AND #$7F
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 FastXfer
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 #$00 ;signaling.
STA ATdataHB,x ;Clear high byte data latch
LDA #$06 ;Reset bit=1, Disable INTRQ=1
STA ATAdCtrl,x
JSR Norm_Out ;Per ATA-6 spec, need to wait 5us minimum.
LDA #$02 ;Reset bit=0, Disable INTRQ=1
STA ATAdCtrl,x
LDY #$d0 ; 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 #$c8 ;200.
LDX SlotCX
LDA ClrCSMsk,x ;reset MASK bit in PLD for normal CS0
@1: LDA ATA_Stat,x ;signaling.
BMI @1
AND #$08 ;%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 #$c8 ;200.
@3: LDA ATA_Stat,x
BMI @3
AND #$E9 ;%11101001 Check if device is ready to
CMP #$40 ;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 #$00
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 #$00 ;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 #$09 ;Check for error response from device
CMP #$08 ;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 #$09 ;Check for error response from device
CMP #$08 ;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 #$09 ;Check for error response from device
CMP #$01 ;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 #$09 ;Check for error response from device
CMP #$01 ;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 #$09 ;Check for error response from device
CMP #$01 ;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 #$09 ;Check for error response from device
CMP #$01 ;If DF=1 or DRQ=0 or ERR=1 a device error
BEQ Write_Err
JMP CSet2Mhz ;exit write routines