Files
RomWBW/Source/BPBIOS/hardide.z80
2015-03-23 01:50:45 +00:00

469 lines
16 KiB
Z80 Assembly
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;::::::::::::::::::::::::::::::::::::::::::::::::**************************
; 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 ===========================