; ;================================================================================================== ; IDE DISK DRIVER ;================================================================================================== ; ; IO PORT ADDRESSES ; #IF (IDEMODE == IDEMODE_MK4) IDEBASE .EQU MK4_IDE #ELSE IDEBASE .EQU $20 #ENDIF #IF ((IDEMODE == IDEMODE_DIO) | (IDEMODE == IDEMODE_MK4)) #IF (IDE8BIT) IDEDATA: .EQU $IDEBASE + $00 ; DATA PORT (8 BIT) #ELSE IDEDATALO: .EQU $IDEBASE + $00 ; DATA PORT (16 BIT LO BYTE) IDEDATAHI: .EQU $IDEBASE + $08 ; DATA PORT (16 BIT HI BYTE) IDEDATA: .EQU IDEDATALO #ENDIF #ENDIF ; #IF (IDEMODE == IDEMODE_DIDE) #IF (IDE8BIT) IDEDATA: .EQU $IDEBASE + $00 ; DATA PORT (8 BIT OR 16 BIT PIO LO/HI BYTES) #ELSE IDEDATA: .EQU $IDEBASE + $08 ; DATA PORT (16 BIT PIO LO/HI BYTES) IDEDMA: .EQU $IDEBASE + $09 ; DATA PORT (16 BIT DMA LO/HI BYTES) #ENDIF #ENDIF ; IDEERR: .EQU $IDEBASE + $01 ; READ: ERROR REGISTER; WRITE: PRECOMP IDESECTC: .EQU $IDEBASE + $02 ; SECTOR COUNT IDESECTN: .EQU $IDEBASE + $03 ; SECTOR NUMBER IDECYLLO: .EQU $IDEBASE + $04 ; CYLINDER LOW IDECYLHI: .EQU $IDEBASE + $05 ; CYLINDER HIGH IDEDEVICE: .EQU $IDEBASE + $06 ; DRIVE/HEAD IDESTTS: .EQU $IDEBASE + $07 ; READ: STATUS; WRITE: COMMAND IDECTRL: .EQU $IDEBASE + $0E ; READ: ALTERNATIVE STATUS; WRITE; DEVICE CONTROL IDEADDR: .EQU $IDEBASE + $0F ; DRIVE ADDRESS (READ ONLY) ; ; ; IDECMD_READ .EQU 020H IDECMD_WRITE .EQU 030H IDECMD_SETFEAT .EQU 0EFH ; IDERC_OK .EQU 0 IDERC_CMDERR .EQU 1 IDERC_RDYTO .EQU 2 IDERC_BUFTO .EQU 3 ; ; UNIT CONFIGURATION ; IDE0_DEVICE .DB 11100000B ; LBA, MASTER DEVICE IDE1_DEVICE .DB 11110000B ; LBA, SLAVE DEVICE ; ; ; IDE_DISPATCH: LD A,B ; GET REQUESTED FUNCTION AND $0F JR Z,IDE_RD DEC A JR Z,IDE_WR DEC A JR Z,IDE_ST DEC A JR Z,IDE_MED CALL PANIC ; IDE_RD: JP IDE_READ IDE_WR: JP IDE_WRITE IDE_ST: JP IDE_STATUS IDE_MED: JP IDE_MEDIA ; ; IDE_MEDIA ; IDE_MEDIA: LD A,MID_HD RET ; ; ; IDE_INIT: PRTS("IDE: IO=0x$") LD A,IDEDATA CALL PRTHEXBYTE PRTS(" UNITS=2$") ; CALL IDE_RESET XOR A DEC A ; INITIAL STATUS IS NOT READY $FF LD (IDE_STAT),A ; SAVE IT RET ; ; ; IDE_STATUS: LD A,(IDE_STAT) ; LOAD STATUS OR A ; SET FLAGS RET ; ; ; IDE_READ: LD A,IDECMD_READ LD (IDE_CMD),A JP IDE_RW ; ; ; IDE_WRITE: LD A,IDECMD_WRITE LD (IDE_CMD),A JP IDE_RW ; ; ; IDE_RW: ; CLEAR RESULTS XOR A ; A = 0 LD (IDE_RC),A ; CLEAR RETURN CODE LD (IDE_STTS),A ; CLEAR SAVED STTS LD (IDE_ERRS),A ; CLEAR SAVED ERR ; INIT REQUIRED? LD A,(IDE_STAT) ; GET CURRENT STATUS OR A ; SET FLAGS JR Z,IDE_RW0 ; IF STATUS OK, BYPASS RESET CALL IDE_RESET ; DO THE RESET #IF (IDE8BIT) CALL IDE_WAITRDY LD A,01H OUT (IDEERR),A LD A,IDECMD_SETFEAT OUT (IDESTTS),A CALL IDE_WAITRDY JP NC,IDE_ERR CALL IDE_CHKERR ; CHECK FOR ERRORS JP NC,IDE_ERR #IF (IDETRACE >= 2) CALL IDE_PRT #ENDIF #ENDIF IDE_RW0: CALL IDE_WAITRDY ; WAIT FOR DRIVE READY JP NC,IDE_ERR CALL IDE_SETUP ; SETUP CYL, TRK, HEAD LD A,(IDE_CMD) OUT (IDESTTS),A CALL IDE_WAITRDY ; WAIT FOR DRIVE READY JP NC,IDE_ERR CALL IDE_CHKERR ; CHECK FOR ERRORS JP NC,IDE_ERR CALL IDE_WAITBUF ; WAIT FOR BUFFER READY JP NC,IDE_ERR LD A,(IDE_CMD) ; DISPATCH TO READ OR WRITE SPECIFIC LOGIC CP IDECMD_WRITE JP Z,IDE_RW1 CALL IDE_BUFRD ; READ BUFFER CALL IDE_WAITRDY ; WAIT FOR DRIVE READY JP NC,IDE_ERR CALL IDE_CHKERR ; CHECK FOR ERRORS JP NC,IDE_ERR JP IDE_OK IDE_RW1: CALL IDE_BUFWR ; WRITE BUFFER CALL IDE_WAITRDY ; WAIT FOR DRIVE READY JP NC,IDE_ERR CALL IDE_CHKERR ; CHECK FOR ERRORS JP NC,IDE_ERR JP IDE_OK IDE_ERR: XOR A DEC A ; A = $FF TO SIGNAL ERROR LD (IDE_STAT),A ; SAVE IT #IF (IDETRACE >= 1) PUSH AF CALL IDE_PRT POP AF #ENDIF RET IDE_OK: #IF (IDETRACE >= 2) CALL IDE_PRT #ENDIF XOR A RET ; ; ; IDE_RESET: LD A,000001110B ; NO INTERRUPTS, ASSERT RESET BOTH DRIVES OUT (IDECTRL),A LD DE,8 ; DELAY ABOUT 200ms CALL VDELAY LD A,000001010B ; NO INTERRUPTS, DEASSERT RESET OUT (IDECTRL),A XOR A LD (IDE_STAT),A ; STATUS OK RET ; SAVE IT ; ; ; IDE_WAITRDY: LD DE,0 ; TIMEOUT IS 250us * 65536 = 15 SECONDS IDE_WBSY: PUSH DE LD DE,10 ; INNER LOOP DELAY IS 250us (25us * 10) CALL VDELAY POP DE DEC DE LD A,D OR E JP Z,IDE_TO IN A,(IDESTTS) ; READ STATUS LD (IDE_STTS),A ; SAVE IT AND 011000000b ; ISOLATE BUSY AND RDY BITS XOR 001000000b ; WE WANT BUSY(7) TO BE 0 AND RDY(6) TO BE 1 JP NZ,IDE_WBSY SCF ; CARRY 1 = OK RET IDE_TO: LD A,IDERC_RDYTO LD (IDE_RC),A XOR A ; CARRY 0 = TIMEOUT RET ; ; ; IDE_CHKERR: IN A,(IDESTTS) ; GET STATUS LD (IDE_STTS),A ; SAVE IT AND 000000001B ; ERROR BIT SET? SCF ; ASSUME NO ERR RET Z ; NO ERR, RETURN WITH CF SET IN A,(IDEERR) ; READ ERROR FLAGS LD (IDE_ERRS),A ; SAVE IT LD A,IDERC_CMDERR ; COMMAND ERROR LD (IDE_RC),A ; SAVE IT OR A ; CLEAR CF TO SIGNAL ERROR RET ; ; ; IDE_WAITBUF: LD DE,0 IDE_WDRQ: CALL DELAY INC DE LD A,D OR E JP Z,IDE_TO2 IN A,(IDESTTS) ; WAIT FOR DRIVE'S 512 BYTE READ BUFFER LD (IDE_STTS),A ; SAVE IT AND 000001000B ; TO FILL (OR READY TO FILL) JP Z,IDE_WDRQ SCF ; CARRY 1 = OK RET IDE_TO2: LD A,IDERC_BUFTO LD (IDE_RC),A XOR A ; CARRY 0 = TIMED OUT RET ; ; ; IDE_BUFRD: LD HL,(DIOBUF) LD B,0 #IF (IDE8BIT | (IDEMODE == IDEMODE_DIDE)) LD C,IDEDATA INIR INIR #ELSE LD C,IDEDATAHI IDE_BUFRD1: IN A,(IDEDATALO) ; READ THE LO BYTE LD (HL),A ; SAVE IN BUFFER INC HL ; INC BUFFER POINTER INI ; READ AND SAVE HI BYTE, INC HL, DEC B JP NZ,IDE_BUFRD1 ; LOOP AS NEEDED #ENDIF RET ; ; ; IDE_BUFWR: LD HL,(DIOBUF) LD B,0 #IF (IDE8BIT | (IDEMODE == IDEMODE_DIDE)) LD C,IDEDATA OTIR OTIR #ELSE LD C,IDEDATAHI IDE_BUFWR1: LD A,(HL) ; GET THE LO BYTE AND KEEP IT IN A FOR LATER INC HL ; BUMP TO NEXT BYTE IN BUFFER OUTI ; WRITE HI BYTE, INC HL, DEC B OUT (IDEDATALO),A ; NOW WRITE THE SAVED LO BYTE TO LO BYTE JP NZ,IDE_BUFWR1 ; LOOP AS NEEDED #ENDIF RET ; ; ; IDE_SETUP: LD A,1 OUT (IDESECTC),A LD A,(HSTDSK) ; HSTDSK -> HEAD BIT 4 TO SELECT UNIT AND 0FH CP 0 JP Z,IDE_SETUP_UNIT0 CP 1 JP Z,IDE_SETUP_UNIT1 CALL NC,PANIC IDE_SETUP_UNIT0: LD A,(IDE0_DEVICE) ; LD DE,(IDE0_OFFSET) JP IDE_SETUP1 IDE_SETUP_UNIT1: LD A,(IDE1_DEVICE) ; LD DE,(IDE1_OFFSET) JP IDE_SETUP1 IDE_SETUP1: LD (IDE_DEVICE),A OUT (IDEDEVICE),A ; SEND 3 BYTES OF LBA (HSTTRK:HSTSEC) T:SS -> CYL:SEC (CC:S) LD A,(HSTTRK) ; HSTTRK LSB LD (IDE_CYLHI),A ; SAVE IT OUT (IDECYLHI),A ; -> CYLINDER HI LD A,(HSTSEC + 1) ; HSTSEC MSB LD (IDE_CYLLO),A ; SAVE IT OUT (IDECYLLO),A ; -> CYLINDER LO LD A,(HSTSEC) ; HSTSEC LSB LD (IDE_SEC),A ; SAVE IT OUT (IDESECTN),A ; -> SECTOR NUM #IF (DSKYENABLE) CALL IDE_DSKY #ENDIF RET ; ; ; #IF (DSKYENABLE) IDE_DSKY: LD HL,DSKY_HEXBUF LD A,(IDE_DEVICE) LD (HL),A INC HL LD A,(IDE_CYLHI) LD (HL),A INC HL LD A,(IDE_CYLLO) LD (HL),A INC HL LD A,(IDE_SEC) LD (HL),A CALL DSKY_HEXOUT RET #ENDIF ; ; ; IDE_PRT: CALL NEWLINE LD DE,IDESTR_PREFIX CALL WRITESTR CALL PC_SPACE LD DE,IDESTR_CMD CALL WRITESTR LD A,(IDE_CMD) CALL PRTHEXBYTE CALL PC_SPACE CALL PC_LBKT LD A,(IDE_CMD) LD DE,IDESTR_READ CP IDECMD_READ JP Z,IDE_PRTCMD LD DE,IDESTR_WRITE CP IDECMD_WRITE JP Z,IDE_PRTCMD LD DE,IDESTR_UNKCMD IDE_PRTCMD: CALL WRITESTR CALL PC_RBKT CALL PC_SPACE LD A,(IDE_DEVICE) CALL PRTHEXBYTE LD A,(IDE_CYLHI) CALL PRTHEXBYTE LD A,(IDE_CYLLO) CALL PRTHEXBYTE LD A,(IDE_SEC) CALL PRTHEXBYTE CALL PC_SPACE LD DE,IDESTR_ARROW CALL WRITESTR CALL PC_SPACE IN A,(IDESTTS) CALL PRTHEXBYTE CALL PC_SPACE IN A,(IDEERR) CALL PRTHEXBYTE CALL PC_SPACE LD DE,IDESTR_RC CALL WRITESTR LD A,(IDE_RC) CALL PRTHEXBYTE CALL PC_SPACE CALL PC_LBKT LD A,(IDE_RC) LD DE,IDESTR_RCOK CP IDERC_OK JP Z,IDE_PRTRC LD DE,IDESTR_RCCMDERR CP IDERC_CMDERR JP Z,IDE_PRTRC LD DE,IDESTR_RCRDYTO CP IDERC_RDYTO JP Z,IDE_PRTRC LD DE,IDESTR_RCBUFTO CP IDERC_BUFTO JP Z,IDE_PRTRC LD DE,IDESTR_RCUNK IDE_PRTRC: CALL WRITESTR CALL PC_RBKT RET ; ; ; IDESTR_PREFIX .TEXT "IDE:$" IDESTR_CMD .TEXT "CMD=$" IDESTR_RC .TEXT "RC=$" IDESTR_ARROW .TEXT "-->$" IDESTR_READ .TEXT "READ$" IDESTR_WRITE .TEXT "WRITE$" IDESTR_UNKCMD .TEXT "UNKCMD$" IDESTR_RCOK .TEXT "OK$" IDESTR_RCCMDERR .TEXT "COMMAND ERROR$" IDESTR_RCRDYTO .TEXT "READY TIMEOUT$" IDESTR_RCBUFTO .TEXT "BUFFER TIMEOUT$" IDESTR_RCUNK .TEXT "UNKNOWN ERROR$" ; ;================================================================================================== ; IDE DISK DRIVER - DATA ;================================================================================================== ; IDE_STAT .DB 0 IDE_RC .DB 0 ; IDE_CMD: .DB 0 IDE_DEVICE .DB 0 IDE_CYLHI: .DB 0 IDE_CYLLO: .DB 0 IDE_SEC: .DB 0 IDE_STTS: .DB 0 IDE_ERRS .DB 0 ; ; ; ; ; Error Register (ERR bit being set in the Status Register) ; ; Bit 7: BBK (Bad Block Detected) Set when a Bad Block is detected. ; Bit 6: UNC (Uncorrectable Data Error) Set when Uncorrectable Error is encountered. ; Bit 5: MC (Media Changed) Set to 0. ; Bit 4: IDNF (ID Not Found) Set when Sector ID not found. ; Bit 3: MCR (Media Change Request) Set to 0. ; Bit 2: ABRT (Aborted Command) Set when Command Aborted due to drive error. ; Bit 1: TKONF (Track 0 Not Found) Set when Executive Drive Diagnostic Command. ; Bit 0: AMNF (Address mark Not Found) Set in case of a general error. ; ; Status Register (When the contents of this register are read by the host, the IREQ# bit is cleared) ; ; Bit 7: BSY (Busy) Set when the drive is busy and unable to process any new ATA commands. ; Bit 6: DRDY (Data Ready) Set when the device is ready to accept ATA commands from the host. ; Bit 5: DWF (Drive Write Fault) Always set to 0. ; Bit 4: DSC (Drive Seek Complete) Set when the drive heads have been positioned over a specific track. ; Bit 3: DRQ (Data Request) Set when device is ready to transfer a word or byte of data to or from the host and the device. ; Bit 2: CORR (Corrected Data) Always set to 0. ; Bit 1: IDX (Index) Always set to 0. ; Bit 0: ERR (Error) Set when an error occurred during the previous ATA command.