;::::::::::::::::::::::::::::::::::::::::::::::::************************** ; 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 ===========================