#TITLE "NEC-APC HARD DISK DRIVER" #SUBTTL "Copyright (c) Multi-Micro Corp." #PAGE 132,66 ; ; HDCAPC.A ; Hard disk driver for the NEC-APC and TurboDOS. ; ; MODULE "HDCAPC" ; module ID ; #INCLUDE "DREQUATE" ; common driver equivalences #INCLUDE "APCEQU" ; NEC-APC equivalences ; LOC Data# ; locate in data segment ; DMXSPH: ; mutual exclusion semaphore WORD 1 ; semaphore count WORD . ; semaphore P/D head WORD .-2 ; DWTSPH: ; disk wait semaphore WORD 0 ; semaphore count WORD . ; semaphore P/D head WORD .-2 ; ; Driver temporaries and variables ; DEBUG == "Debug (0 = no, 1 = yes)" DRVRBUSY: BYTE 0 ; Set 0 if clock can flush buf CURDRIVE: BYTE 0XFF ; Drive owning track buffer CURCYLDR: WORD 0XFFFF ; Track number in track buffer RETRIES: BYTE 0 ; Number of retries left MAXRTRY == 5 WRFLAG: BYTE 0 ; Flag set 1 if write, 0 if read HDBUFSEG: WORD 0X9C00 ; Segment containing track buf DIRTYBUF: BYTE 0 ; Flag set 1 if track buf dirty CLKCHK: BYTE 0 ; Number of ticks since check ; ; Command blocks and related declarations ; SEEKOP == 0X68 ; Seek operation command SEEKCMD == . ; Seek command block BYTE 2 ; Number of parameters to seek SEEKCYL: WORD 0 ; Hi byte, Lo byte of cylinder SEEKCDRV: BYTE SEEKOP ; Seek command with drive ; RECALOP == 0X50 ; Recalibrate operation command RECALCMD == . ; Recalibrate command block BYTE 0 ; Number of parameters to recal RCDRV: BYTE RECALOP ; Recal command with drive ; READCMD == 0XB0 ; Read operation command DMAREAD == 0X15 ; DMA read mode command XFERCMD == . ; Read/write command block BYTE 6 ; Number of parameters to xfer XPHEAD: BYTE 0 ; Physical head number XCYL: WORD 0 ; Hi byte, Lo byte of cylinder XLHEAD: BYTE 0 ; Logical head number BYTE 0 ; Starting sector number BYTE 0X20 ; Number of sectors XCDRV: BYTE 0 ; I/O command with drive STATBUF: RES 8 ; Status byte buffer ; ; Temporary PDR ; LOCPDR: RES PDRLEN ; PDR for actual transfer ; ; Disk descriptor table ; DSTTBL: DB 0X80+0X06 ; Fixed disk 8K byte AU DW 1440 ; Number of AUs on disk DB 2 ; Number of AUs for directory DB 0 ; Sector size 128 DW 64 ; 64 sectors per track DW 1440 ; Number of tracks per disk DW 8 ; Number of reserved tracks ; ; Controller, Formatter and DMA port declarations ; DMAMODE == 0X9B ; DMA mode register DMAADDR == 0XA3 ; DMA address register DMATC == 0X93 ; DMA transfer count DMAMASK == 0XAB ; DMA mask register DMACMD == 0XA9 ; DMA command register DMAMK == 0X9F ; DMA mask register DMAIMK == 0X98 ; DMA interrupt mask register INTREG == 0X96 ; Interrupt control/status FMTREG == 0XA2 ; Formatter control/status HDCDATA == 0XA0 ; Controller data register HDCCMD == 0X92 ; Controller command register RESET == 0X94 ; Controller master reset reg HDCSTAT == 0X92 ; Controller status register ; ; ; ; Function branching table. ; FCNTBL: WORD READ ; function=0, disk read WORD WRITE ; function=1, disk write WORD RETDST ; function=2, return specifications WORD RETRDY ; function=3, return ready WORD FMTTRK ; function=4, format track NMBFCN == (.-FCNTBL)/2 ; number of supported functions ; ; INMSG: BYTE "Into drvr",13,10,0 OUTMSG: BYTE "Out of drvr",13,10,0 LOC Code# ; locate in code segment PRINT: PUSH AX PUSH CX PUSH DX __1: MOV CL,[BX] OR CL,CL JZ __2 PUSH BX CALL SCREEN# POP BX INC BX JMP __1 __2: POP DX POP CX POP AX RET ; ; Driver initialization. ; This entry point is called once at system cold boot time and may ; be used to perform any neccessary device initialization. ; #IF DEBUG DSKINX:: #ELSE DSKIN_:: #ENDIF XOR AL,AL ; Reset hard disk OUT RESET,AL OUT FMTREG,AL MOV AL,=0X80 ; Perform black magic OUT FMTREG,AL XOR AL,AL OUT FMTREG,AL OUT DMACMD,AL ; Clear out DMA command reg MOV AL,=0X0E ; Clear out DMA mask reg OUT DMAMK,AL XOR AL,AL ; Clear out interrupt mask OUT DMAIMK,AL LEA BX,RECALCMD ; Issue recalibrate command CALL SENDCMD ; RET ; end of initialization ; ; ; Driver entry point. ; #IF DEBUG DSKDRX:: #ELSE DSKDR_:: #ENDIF MOV BX,&DMXSPH ; get mutual exclusion semaphore CALL WAIT# ; dispatch if necessary CALL __E ; call the disk driver PUSH AX ; save results MOV BX,&DMXSPH ; get mutual exclusion semaphore CALL SIGNAL# ; signal process as ready POP AX ; restore results RET ; done ; __E: MOV BL,PDRFCN[SI] ; get PD request function number CMP BL,=NMBFCN ; check max function number JB __C ; if valid, continue XOR AL,AL ; set error status RET ; and exit ; __C: XOR BH,BH ; make double word ADD BX,BX ; times 2 for table lookup JMPI FCNTBL[BX] ; off to the driver function ; ; ; Function=2, Return disk specifications. ; RETDST: LEA AX,DSTTBL ; Get address of descriptor MOV PDRDST[SI],AX JMP RETRDY ; Return ready status ; ; Branch here if device read errors or format cannot be found ; FMTERR: XOR AL,AL ; force error status RET ; and exit ; ; ; Function=4, format a track ; FMTTRK: OR AL,=0XFF ; force result error RET ; and exit unsupported routine ; ; ; Function=0, read disk ; READ: MOV WRFLAG,=00 ; Clear write flag JMPS RWCOM ; and join common code ; ; ; Function=1, write disk ; WRITE: MOV WRFLAG,=01 ; Set write flag ; ; Common routines to both reading and writing. ; RWCOM: LEA BX,INMSG CALL PRINT MOV CL,=06 ; Multiply track by 64 for MOV AX,PDRTRK[SI] ; ...virtual sector # SHL AX,CL ADD AX,PDRSEC[SI] MOV CX,=52 ; Calculate actual track/sector XOR DX,DX DIV CX ; AX := Track, DX := Sector SUB CX,DX ; CX := Sectors left in track LEA BX,LOCPDR ; Get address of local PDR MOV PDRTRK[BX],AX ; Save actual track number MOV PDRSEC[BX],DX ; Save actual sector number MOV AX,PDRSC[SI] ; Get sector transfer count CMP CX,AX ; Does I/O go off track ? JG __1 MOV AX,CX ; Yes, use actual count __1: MOV PDRSC[BX],AX ; Save new sector count MOV CL,=07 ; Calculate byte count as SHL AX,CL ; ... sector count * 128 MOV PDRTC[BX],AX ; Save new byte count MOV AL,PDRDRV[SI] ; Move drive number across MOV PDRDRV[BX],AL MOV AX,PDRDMA[SI] ; Move DMA start address across MOV PDRDMA[BX],AX MOV AX,PDRBAS[SI] ; Move DMA segment across MOV PDRBAS[BX],AX PUSH SI ; Save address of real PDR MOV SI,BX ; Pass in fako PDR CALL __COMN ; Perform actual I/O OR AL,AL ; Was I/O operation good ? JNZ __99 POP BX ; Yes, see about more I/O MOV AX,PDRTC[SI] ; Get old transfer count MOV CX,AX SUB AX,PDRTC[BX] ; Is there anything left ? JZ __98 ADD PDRDMA[SI],CX ; Yes, move buffer pointer up INC WORD PDRTRK[SI] ; Move to next track MOV WORD PDRSEC[SI],=0000 ; Move to first sector NEG AX ; Get number of bytes left MOV PDRTC[SI],AX ; Save new number of bytes MOV CL,=07 ; Calculate number of sectors SHR AL,CL MOV PDRSC[SI],AX ; Save new number of sectors PUSH BX ; Save original PDR again CALL __COMN __99: POP BX ; Restore original PDR __98: MOV SI,BX LEA BX,OUTMSG CALL PRINT RET __COMN: MOV DRVRBUSY,=01 ; Do not let clock in MOV DL,PDRDRV[SI] ; Get new drive number MOV AX,PDRTRK[SI] ; Get new track number MOV DH,AL ; New head is bits 0:2 AND DH,=07 MOV BX,AX ; New cylinder is bits 3:15 SHR BX,=1 SHR BX,=1 SHR BX,=1 XCHG BH,BL ; NEC wants hi byte, low byte CMP DL,CURDRIVE ; Are we accessing buffered trk? JNE __FLUSH CMP BX,CURCYLDR JE LOOPIO __FLUSH:PUSH AX ; No, prepare to write and seek PUSH BX PUSH DX CALL DISKFLS ; Write track buffer if dirty MOV SEEKCYL,BX ; Setup seek command block AND SEEKCDRV,=0XF8 ; Eliminate drive from block OR SEEKCDRV,DL ; Include new drive AND RCDRV,=0XF8 ; Eliminate drive for recal cmd OR RCDRV,DL ; Include new drive MOV RETRIES,=MAXRTRY ; Setup retry counter __C: LEA BX,SEEKCMD ; Issue seek command CALL SENDCMD JE STREAD ; Was seek successful ? DEC RETRIES ; No, are there more retries ? JE SEEKERR LEA BX,RECALCMD ; Yes, recalibrate disk CALL SENDCMD JMP __C ; Go reissue seek command SEEKERR: ADD SP,=6 ; Trash saved parameters XFERERR: MOV AL,=0XFF ; Return bad result RET STREAD: POP DX ; Restore saved parameters POP BX POP AX LOOPIO: MOV XPHEAD,DH ; Setup parameters for xfer cmd MOV XLHEAD,DH OR BL,=0XFE ; Set unused bits in cylinder MOV XCYL,BX MOV XCDRV,DL ; Set drive, but mash command TEST WRFLAG,=01 ; Is it a write command ? JZ DOREAD CMP WORD PDRSEC[SI],=0000 ; Yes, should we read track ? JNZ DOREAD CMP WORD PDRTC[SI],=0X2000 JZ MOVEDATA DOREAD: OR XCDRV,=READCMD ; Prepare for a read operation MOV RETRIES,=MAXRTRY ; Setup retry counter XFERRTY: MOV AL,=DMAREAD ; Setup DMA for read CALL SETDMA LEA BX,XFERCMD ; Start up read operation CALL SENDCMD JE MOVEDATA ; Was the read successful ? DEC RETRIES ; No, are there retries left ? JZ XFERERR LEA BX,RECALCMD ; Yes, recalibrate the disk CALL SENDCMD LEA BX,SEEKCMD ; Seek back to desired track CALL SENDCMD JMP XFERRTY ; Go back and try reading again MOVEDATA: PUSH DS ; Save source data segment PUSH DI ; Save destination register CLD ; Make sure transfer is forward MOV ES,HDBUFSEG ; Load destination for write MOV DI,PDRSEC[SI] MOV CL,=7 SHL DI,CL MOV CX,PDRTC[SI] ; Get byte count MOV BX,PDRDMA[SI] ; Load source for write TEST WRFLAG,=01 ; Set flags for type of call MOV DS,PDRBAS[SI] XCHG BX,SI JNZ MVWRDS ; Is this a read call ? XCHG SI,DI ; Yes, swap source and dest MOV AX,DS MOV DX,ES MOV DS,DX MOV ES,AX MVWRDS: SHR CX,=1 ; Convert byte count to words REP MOVS WORD ; Transfer from/to buffer JNC __R ; Odd number of bytes ? MOVS BYTE ; Yes, catch last byte __R: MOV SI,BX ; Recover critical registers POP DI ; Pop destination register POP DS MOV AL,WRFLAG ; Set dirty bit if write OR DIRTYBUF,AL XOR AL,AL ; Reset clock timeout MOV CLKCHK,AL MOV DRVRBUSY,AL RET SETDMA: OUT DMAMODE,AL ; Setup read/write mode CALL DELAY ; Couldn't hurt XOR AL,AL ; Transfer at start of buffer OUT DMAADDR,AL ; Setup low part of address CALL DELAY OUT DMAADDR,AL ; Setup high part of address CALL DELAY MOV AL,=0X1FFF % 0X100 ; Get low part of track size OUT DMATC,AL CALL DELAY MOV AL,=0X1FFF / 0X100 ; Get high part of track size OUT DMATC,AL CALL DELAY MOV AL,=01 ; Set DMA mask OUT DMAMASK,AL CALL DELAY RET DELAY: PUSH CX ; Save counter register PUSHF ; Save flags MOV CX,=0X10 ; Setup count __L: LOOP __L ; Wait for a while POPF ; Restore flags and counter POP CX RET SENDCMD: XOR AL,AL ; Reset completion status OUT INTREG,AL CALL DELAY ; Couldn't hurt XOR CH,CH ; Get number of parameters MOV CL,[BX] JCXZ __CMD ; Are there any parameters ? MOV AL,=0X40 ; Write CLDB to formatter OUT FMTREG,AL CALL DELAY __FWT: IN AL,FMTREG ; Can formatter accept byte ? TEST AL,=0X20 JZ __FWT __PMOR: INC BX ; Get next parameter DEC CX ; Decrement parameter count JCXZ __PLST ; Is this the last parameter ? MOV AL,[BX] ; No, get the parameter CALL DELAY OUT HDCDATA,AL ; Output it to controller CALL DELAY JMP __PMOR ; Process next parameter __PLST: MOV AL,=0X04 ; Tell controller this is last CALL DELAY OUT FMTREG,AL CALL DELAY MOV AL,[BX] ; Get last parameter OUT HDCDATA,AL CALL DELAY __CMD: MOV AL,=0X11 ; Send HSRQ and CLCE to fmtr OUT FMTREG,AL CALL DELAY __CE: IN AL,FMTREG ; Wait until formatter ready TEST AL,=0X04 JNZ __CE INC BX ; Load command byte MOV AL,[BX] OUT HDCCMD,AL ; Send command byte to cntrlr MOV AH,AL ; Save command byte for later CALL DELAY __CWT: IN AL,INTREG ; Is the command done yet? TEST AL,=0X02 JZ __CWT CALL DELAY ; Yes, finally AND AH,=0X80 ; Yes, was it a seek command ? JZ IOSTAT MOV CX,=07 ; Yes, setup for normal status JMP READST IOSTAT: IN AL,HDCSTAT ; Clear status register CALL DELAY MOV AL,=0X10 ; Set formatter CLCE OUT FMTREG,AL CALL DELAY __CEWT: IN AL,FMTREG ; Wait until CE goes down TEST AL,=0X04 JNZ __CEWT CALL DELAY __HDIN: IN AL,INTREG ; Wait until HDIN goes down TEST AL,=0X02 JZ __HDIN CALL DELAY __SWAIT:IN AL,HDCSTAT ; Wait until status is posted TEST AL,=0X10 JZ __SWAIT MOV AL,0X11 ; Set formatter HSRQ and CLCE CALL DELAY OUT FMTREG,AL CALL DELAY __CE: IN AL,FMTREG ; Wait until CE is down again TEST AL,=0X04 JNZ __CE CALL DELAY MOV AL,=0X10 ; Issue interrupt status command OUT HDCCMD,AL CALL DELAY __SW: IN AL,INTREG ; Wait until status is ready TEST AL,=0X02 JZ __SW MOV CX,=01 ; Setup status byte count READST: MOV BX,DS ; Setup pointer to status buf PUSH ES MOV ES,BX PUSH DI ; Save destination register LEA DI,STATBUF CALL DELAY IN AL,HDCSTAT ; Get status byte CLD STOS BYTE ; Save status byte in buffer MOV AL,=0X20 ; Setup formatter for status read CALL DELAY OUT FMTREG,AL CALL DELAY __FWT: IN AL,FMTREG ; Is formatter ready with stat? TEST AL,=0X20 JZ __FWT __SLP: IN AL,HDCDATA ; Get status byte STOS BYTE LOOP __SLP MOV AL,=0X10 ; Set CLCE in formatter CALL DELAY OUT FMTREG,AL CALL DELAY __CE: IN AL,FMTREG ; Wait until CE goes down TEST AL,=0X04 JNZ __CE MOV AX,STATBUF ; Get controller status AND AL,=0X60 ; Mask irrelevant bits CMP AL,=0X40 ; Was operation successful ? POP ES POP DI ; Recover destination register RET DISKFLS: XOR AL,AL RET ; ; ; Function=3, return drive ready status ; RETRDY: MOV AL,PDRDRV[SI] ; Return ready code DEC AL RET END