You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

469 lines
16 KiB

;::::::::::::::::::::::::::::::::::::::::::::::::**************************
; Hard disk routines as implemented for ** Hardware Dependent **
; Tilmann Reh's GIDE board adapted to the ** for exact interface **
; D-X Designs Pty Ltd P112. **************************
; Portions derived from GIDE.Z80 with tips from GIDETEST.PAS by Tilmann Reh.
;--------------------------------------------------------------------------
; This file uses modifications of the definitions in ICFG-xx to reflect
; Physical and/or logical definitions for IDE drives. A controller type of
; 8xH signifies IDE/ATA drives, in which case the Drive byte at HDRVx is:
; 7 6 5 4 3 2 1 0
; | | | | | | | +- Unit Number (0 = Master, 1 = Slave)
; | | | | +-+-+--- (reserved)
; | | | +--------- 1 = Active, 0 = Inactive
; +-+-+----------- (reserved)
; Additionally, the first byte of the Reduced Write Cylinder word is re-
; defined to be the number of physical/logical Sectors-Per-Track.
; These parameters are used to convert the Track & 16 Sector/Track format
; assumed in the B/P Bios definitions for Hard Drives into Track/Sector/Head
; Sector Number needed for IDE/ATA Data accesses. Direct driver IO routines
; to Select (SELHD), Read (HDREAD) and Write (HDWRIT) are all included here.
;--------------------------------------------------------------------------
; 1.0 - 26 Aug 01 - Cleaned up source and included fixes from SCSI. HFB
; 0.2 - 28 Jun 97 - Added Home Drive, Retry Disable bit handling. HFB
; 0.1 - 25 Apr 97 - Initial Test Release HFB
;***************************************************************************
IF BANKED
COMMON /BANK2/
ELSE
CSEG
ENDIF
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; Function 0 - Set User Data Area Adress for Direct Disk IO, Return
; Number of Bytes in the driver Command Block (SCSI-"like")
; For IDE, a minimum of 6 Bytes is needed (Comnd,Trk(2),Sctr,Head,Drive)
; Enter: DE = Address of User Data Area
; Exit : A = Number of bytes available in the Command Block
; Uses : A,HL
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
HDVALS: LD (DATADR),DE ; Save the Users Data Area
LD A,CMDSIZ
RET
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; Function 1 - Set Drive bit Command Block from A
; Enter: A = Drive Byte
; Exit : A = Drive Bit in LSB (00/01H, for Master/Slave)
; Uses : AF
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
HDSLCT: AND 01H ; Strip any garbage
LD (hdUnit),A ; save in Command Block
RET
;========================================================================
; Select Hard Disk (Unit 0/1, Master/Slave) < Internal Bios routine >
SELHD: LD A,(SEKPDN) ; Load Device and Unit # to select
SELHDA: LD C,A ; position for calculations
LD B,HDRV1-HDRV0 ; with size
MLT BC ; Calculate offset into table
LD HL,HDRV0 ; from first Physical drive
ADD HL,BC
LD A,(CNTRLR) ; Set Controller type
BIT 7,A ; Is it IDE/ATA?
JP Z,SELERR ; ..jump if Not to return Error
LD A,(HL) ; Fetch Device/LUN byte
CALL HDSLCT ; setting variables for Device and LUN
; Wait until Drive is ready or 10 Seconds
LD A,100 ; 100-100mS ticks
SelWt: CALL BsyWt ; Wait this long for Drive to come Ready
JP C,SELERR ; ..Error if Timeout
; Else gather drive geometry specified in ICFG-xx
INC HL ; Advance to Number of Tracks Word
LD BC,4 ; Move Number of Tracks (Word)
LD DE,hdTrks ; Number of Heads (Byte)
LDIR ; and Sctrs/Trk to local storage
SetPmV: JP SETPARMS ; then set parameters for DPH/DPB
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; Write to Hard Disk Drive < Internal BIOS Routine >
; Writes from HSTBUF using HSTTRK and HSTSEC to build Block Number.
; NOTE: This routine uses physical drive characteristics from ICFG-xx.
HDWRIT: XOR A
LD (HSTACT),A ; Show no active writes pending
LD HL,7CH*256+CMDWR ; Set the IDE Write Command
JR HDRW ; ..continue
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; Read from Hard Disk Drive < Internal BIOS Routine >
; Reads to HSTBUF using HSTTRK and HSTSEC to build Block Number.
; NOTE: This routine uses physical drive characteristics from ICFG-xx.
; The routine computes a sequential block number (as with SCSI) with
; the algorithm; Trk * 16 + Sector, then computes Head, Sector and Track
; using Physical characteristics (hdHds = Number_of_Heads,
; hdSPT = Sectors_per_Track) according to the algorithm:
;
; Sector := (Block# MOD hdSPT)+1 (* Quotient1 := Block# DIV hdSPT *)
; Head := Quotient1 MOD hdHds (* Quotient2 := Quotient1 DIV hdHds *)
; Track := Quotient2
HDREAD: LD HL,7EH*256+CMDRD ; Set the IDE Read Command
HDRW: LD (hdComd),HL ; save
; Prepare for Disk Read/Write by Preloading all Registers
LD HL,(HSTDPH) ; Get pointer to desired DPH
DEC HL ; back up to Device #
LD A,(HL) ; and fetch
CALL SELHDA ; Select this device from A-Reg
LD BC,4*256+0 ; Count = 4, MSB of Block # = 0
LD HL,(HSTTRK) ; Get requested track
MUL16: ADD HL,HL ; Multiply C,H,L by 16 for 21-bit block #
RL C ; shifting overflow bit to C
DJNZ MUL16 ; ..and looping til * 16
LD A,(HSTSEC) ; Get Logical Host Sector # (4-bits)
ADD A,L ; add in Hi 4-bits of low Block #
LD L,A ; save back
LD A,(hdSPT) ; Get Sctrs-Per-Trk
LD E,A
CALL Divide ; Divide CHL by E
INC A ; Make Sctr # Base at 1
LD (hdSec),A ; (save)
LD A,(hdHds) ; Get Number of Heads
LD E,A
CALL Divide ; Divide CHL (Quotient from above) by E
LD (hdHead),A ; (save)
LD A,H ; Swap
LD H,L ; Bytes
LD L,A ; in Track Word
LD (hdTrkH),HL ; save Quotient (Track, Hi & Lo)
LD A,0AAH
LD (hdErr),A ; Activate Retries in case Reading
LD A,1
LD (hdSCnt),A ; Do only One Sector
LD A,5 ; Give it a few tries
HDOps0: LD (HRTrys),A ; Save Count
CALL GoGIDE ; Try Read/Write Operation
RET Z ; ..quit if Ok
LD A,(HRTrys) ; Else
DEC A ; Any tries remaining?
JR NZ,HDOps0 ; ..loop if So
DEC A ; Else Return 0FFH
RET
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; Function 2 - Direct IDE/ATA driver. This routine performs the function
; described by the command in the HD Command Block with Data area
; addressed by DE. At the end of the function, 512 bytes of data are
; transferred from the Bios IO Buffer to the Users Space set by Fcn 0.
; The IDE/ATA Command Block layout is:
;
; Byte Format R/W/V Init RdID Power Diag PwrSet Home
; 0 [Command ] 50 20/30/40 91 EC E0/E1/E5 90 E2/E3 10
; 1 [BitMap ] 70 7C/(7E) 74 40 00 00 04 40
; 2 [Drv/Hd ] 0AnH 0AnH 0AnH-1 0AnH - - - 0An0
; 3 [Cyl#(Hi)] CylHi CylHi CylHi - - - - -
; 4 [Cyl#(Lo)] CylLo CylLo CylLo - - - - -
; 5 [Sector# ] - Sctr - - - - - -
; 6 [Sctr Cnt] - SCnt NSecs - - - n*5Secs -
; 7 [Err Reg ] - (0AA) - - - - - -
; 8 [Dgtl Out] - - - - - - - -
; Rslts/Stat: Stat Stat Stat Stat SCnt Err - Stat
; Reg Reg Reg Reg Reg Reg Reg
;
; Enter: DE = Pointer to User IDE/ATA Command Block
; "hdComd" contains pre-filled Command Block
; A = 0 if No Data to be Written, FF if User-supplied data to write
; Exit : H = Error Byte value (If any)
; L = Status Byte value (If any)
; A = Status byte, Flags set accordingly.
; Uses : AF,BC,DE,HL
; NOTE : Routine assumes the Command Block is properly configured for the
; desired function and device. Timeout returns 0FFH, Unsupported
; command returns 7FH.
; For external access, It assumes the user has used Functions 0 and 1 to
; set the data transfer source/dest address drive number.
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
HD_RW: PUSH AF ; Save User Data Flag
PUSH DE ; and ptr to User's CDB
CALL FLUSH ; Insure Host Buffer is Free
POP HL ; restore User CDB ptr to HL for move
IF BANKED
CALL SHDBNK ; Load Banks for transfer to System fm User
CALL XMOVE ; and Set
ENDIF ;Banked
LD DE,hdComd
LD BC,10 ; Move a 10-byte block
CALL MOVE ; into the Command area
POP AF ; Restore Flag
OR A ; Any User data to write?
JR Z,HD_RW0 ; ..bypass move if not
IF BANKED
CALL SHDBNK ; Load for move from User's to System Banks
CALL XMOVE ; and Set
ENDIF ;Banked
CALL HDDMOV ; Set to move 512 bytes from User to Hstbuf
CALL MOVE ; Do It!
HD_RW0: CALL GoGIDE ; Set Data Addr and do the operation
PUSH HL ; save Status/Err
IF BANKED
CALL SHDBNK ; Load Bank Numbers
LD A,B ; Swap
LD B,C ; Source
LD C,A ; and Destination Banks
CALL XMOVE ; Set Source/Dest
ENDIF ;Banked
CALL HDDMOV ; set Addresses and Length
EX DE,HL ; write back to User's area
CALL MOVE ; move without affecting status in A
POP HL ; Restore Status/Err bytes for checks
LD A,L ; Get Status
OR A ; set flags
RET ; ..and quit
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Raw GIDE Driver. Assumes Command in hdComd and Parms in Comnd Data Block
GoGIDE:
IF NOWAIT ; If Wait States not desired..
IN0 A,(DCNTL) ; Get current settings
LD (WTSAVE),A ; save for exit
AND 11001111B ; Keep everything but IO Waits
OUT0 (DCNTL),A ; and set to No IO Wait States
ENDIF ;nowait
; Wait approximately 5 Seconds for Target to become Busy.
LD L,0FFH ; Preset Timeout Error Status
LD A,50 ; 50-100mS ticks
CALL BsyWt ; Monitor Timeout while checking Busy Bit
JR C,TIMOUT ; ..Reporting Error if Timed Out
;..else fall thru..
LD HL,hdHead
LD A,(hdUnit) ; Get Drive #
ADD A,A ; Shift
ADD A,A ; Drive Bit
ADD A,A ; into
ADD A,A ; position
OR (HL) ; Add Head # bits
OR 0A0H ; and Fixed Bits
LD (HL),A
LD BC,IDESDH ; Set Port Address (GIDE+0EH)
DEC HL ; back ptr up to hdBMap byte
LD E,(HL) ; fetch
LD D,7 ; and set number of bytes
RL E ; Shift B7 out
GoGID0: RL E ; Test bit to Carry
INC HL ; bump Byte ptr
JR NC,GoGID1 ; ..jump if No byte to write
LD A,(HL) ; Else get byte
OUT (C),A ; set IDE Register
GoGID1: DEC BC ; Down to next Register address
DEC D ; Finished?
JR NZ,GoGID0 ; ..loop if Not
LD HL,HSTBUF ; Always IO to/from Host Buffer
LD BC,IDEDat ; Pre-load Data Reg Adr in C, 0 in B
LD A,(hdComd) ; Get Command Byte
CP CMDVER+2 ; Is it in the range w/retry bit?
JR NC,GoGID2 ; ..jump if Not
AND 0FEH ; Else Ignore Retry disable bit (B0)
CP CMDRD ; Read Sector?
JR Z,HRead
CP CMDWR ; Write Sector?
JR Z,HWrite
CP CMDVER ; Verify Sector(s)?
JR Z,HMisc
BIT 4,A ; Is it Home Comnd?
JR Z,HMisc
;..else fall thru to error exit..
GoGID2: CP CMDID ; Read ID Information?
JR Z,HRead
CP CMDDIAG ; Perform Diagnostics?
JR Z,HDiag
CP CMDINIT ; Initialize Drive Parameters?
JR Z,HInit
CP CMDPWQ ; Query Power Status?
JR Z,HPwrQ
CP CMDFMT ; Format Track?
JR Z,HWrite
CP CMDPW0 ; Low range of Power Set Commands?
JR C,GoGIDX ; ..jump if < 0E0H
CP CMDPW3+1 ; High Range of Power Set Commands?
JR C,HMisc ; ..jump if in [E0..E3]
GoGIDX: RES 7,L ; If Not legal Command, Return Error 7FH
POP AF ; (clear command from stack)
TIMOUT:
IF NOWAIT
LD A,(WTSAVE) ; Get entry Wait state settings
OUT0 (DCNTL),A ; and restore
ENDIF
LD A,L ; get Status Byte
LD (ERFLAG),A ; (store)
OR A ; set flags
RET ; and return
;.....
; Read a Sector from the Disk, or Disk Parameters to the Buffer
HRead: CALL Cmd_Wt ; Send Command in A, Return when Ready
HRead0: IN A,(IDECmd) ; Get Status
BIT 3,A ; Ready?
JR Z,HRead0 ; ..loop if Not
INIR ; Read 512 bytes
INIR ; in two-256 byte sequences
HdFini: CALL Wt_Rdy ; Wait for drive to become Ready
;; -- May need this with some Older Drives that send ECC bytes with no warning!
;; BIT 4,A ; DRQ Shifted?
;; JR Z,HdFnQ ; ..jump if Not
;; IN A,(IDEDat) ; Else Read data reg (ECC Bytes?
;; JR HdFini ; ..loop til no more data
HdFnQ: IN A,(IDECmd) ; Restore byte
AND 10001001B ; Busy, DRQ, or Error?
JR Z,HdFnOk ; ..exit if Ok
LD A,02H ; Else Set Error status = 2
HdFnOk: LD L,A ; store
IN A,(IDEErr) ; Get Any Error Status
LD H,A ; save
JR TIMOUT ; ..and exit thru common location
;.....
; Write a 512-byte Sector to the Disk
HWrite: CALL Cmd_Wt ; Send Command in A, Return when Ready
HWrit2: IN A,(IDECmd)
BIT 3,A ; Data Request?
JR Z,HWrit2 ; ..loop if Not
OTIR ; Else Write 512 bytes
OTIR ; in two-256 byte operations
HInit: CALL Wt_Rdy
JR HdFnQ ; ..and finish off above
;.....
; Execute Drive Diagnostics
HDiag: CALL Cmd_Wt ; Send Command in A, Return when Ready
IN A,(IDEErr) ; Read Status of Tests
JR HdFnOk ; ..exit with Status
;.....
; Query Power Save Status
HPwrQ: CALL Cmd_Wt ; Send Command in A, Return when Ready
IN A,(IDESCnt) ; Get Status (00=Running, FF=Stopped)
JR HdFnOk ; ..exit with Status
;.....
; Miscellaneous Commands such as Set Various Power Control Features
HMisc: CALL Cmd_Wt ; Send Command in A, Return When Ready
JR HdFnQ ; ..exit checking Status
;================== SUPPORT ROUTINES ==================
; Divide 24-bit Number by 8-bit Number returning Quotient and Remainder
; Enter: CHL = 24-bit Unsigned Dividend
; E = 8-bit Unsigned Divisor
; Exit : CHL = 24-bit Quotient
; A = 8-bit Remainder
; Uses : AF,BC,HL
Divide: LD B,24+1 ; 25 times thru Loop
XOR A ; Clear Remainder and Carry
Div: ADC A,A ; Shift Accum Left + Carry
SBC A,E ; Subtract Divisor
JR NC,Div0 ; ..jump if it Worked
ADD A,E ; Else restore Accum & Carry
Div0: CCF ; Flip Carry Bit
ADC HL,HL ; Shift any Carry into
RL C ; Dividend/Quotient
DJNZ Div ; ..loop til Done
RET
;.....
; Wait for Drive to become Ready (With Timeout)
; Enter: A = Number of 100 mS increments to wait before timeout
; Exit : Carry Set (C) if Timeout, else Carry Clear (NC), Unit Ready
; Uses : MTM Timer variable, AF
BsyWt: LD (MTM),A ; Set Timer value
BsyW0: LD A,(MTM) ; Get Current Count
OR A ; Have we timed out?
SCF ; (Set error flag in case)
RET Z ; ..exit w/Error flag if So
IN A,(IDECmd) ; Else Get the Busy Bit
RLA ; Is it BSY?
JR C,BSYW0 ; ..loop if So
RET ; else back to Caller, Carry Clear
;.....
; Send command to the IDE Command Register, fall thru to wait for Ready Status
Cmd_Wt: LD A,(hdComd) ; Get Command Byte in case bit stripped
OUT (IDECmd),A ; Start Operation
;..fall thru to wait for Ready
;.....
; Wait for Drive to become Ready (No Timeout)
; Enter: None
; Exit : None
; Uses : AF
Wt_Rdy: IN A,(IDECmd) ; Get Drive Status
RLA ; Ready?
JR C,Wt_Rdy ; ..loop if Not
RET
;.....
; Set registers for Whole Block Move
HDDMOV: LD HL,(DATADR) ; Get ptr to User's Area
LD DE,HSTBUF ; Pt to local Host Buffer
LD BC,512 ; set length
RET ; ..and return
;.....
; Set banks for Interbank move
IF BANKED
SHDBNK: LD A,(USP-1) ; Get Source Bank Byte
RRA ; shift to
RRA ;
RRA ; Bank #
AND 1FH ; Mask off any Junk
LD C,A ; position
LD A,(SYSBNK) ; Get System Bank
LD B,A ; position it too
RET ; and return
ENDIF ;banked
IF BANKED
COMMON /B2RAM/
ELSE
DSEG
ENDIF
HRTrys: DEFS 1 ; Retry counter storage
hdUnit: DEFS 1 ; IDE Drive (0 = Master, 1 = Slave)
hdTrks: DEFS 2 ; Number of Tracks on IDE Drive
hdHds: DEFS 1 ; Number of Heads on IDE Drive
hdSPT: DEFS 1 ; Number of Sectors-Per-Track on IDE Drive
; IDE Command Block for User Direct Driver Access
hdComd: DEFS 1 ; Command Byte
hdBMap: DEFS 1 ; Bit Map (B6..0) of Following Bytes to Set
hdHead: DEFS 1 ; Head Number/Number of Heads in B3..0
hdTrkH: DEFS 1 ; Hi-Track (Cylinder) Byte
hdTrkL: DEFS 1 ; Lo-Track (Cylinder) Byte
hdSec: DEFS 1 ; Sector Number
hdSCnt: DEFS 1 ; Sector Count
hdErr: DEFS 1 ; Error Reg Value
hdDigO: DEFS 1 ; Digital Output Reg Value
CMDSIZ EQU $-hdComd ; Size of Command Block
DATADR: DEFS 2 ; Pointer to User Buffer Space (user bank)
IF NOWAIT
WTSAVE: DEFS 1 ; Storage for Entry Wait State Setting
ENDIF
;======================= End of HARDIDE ===========================