TITLE ADVANCED DIGITAL HDC1001 HARD DISK BAD SECTOR MAPPER Oct 01 11:00 ;********************************************************* ;** ** ;** ADVANCED DIGITAL HDC1001 HARD DISK CONTROLLER ** ;** ** ;** HARD DISK MAD SECTOR MAPPER ** ;** ** ;** This program is to be used to create ** ;** special HDC1001 formatting to map out bad ** ;** areas of the disk. The program prompts the ** ;** user to enter the head, cylinder and sector ** ;** that the error occured on. The program then ** ;** reads all the readable data on that track, ** ;** calculates a new interleave table with the ** ;** erroneous area tagged with an illegal sector ** ;** number, formats the track, and then ** ;** re-records the data back to the track. This ** ;** program requires a Hardware Description Vector ** ;** for the drive to be fixed. ** ;** ** ;** Written by: ** ;** ** ;** Greg Lindberg ** ;** ** ;********************************************************* .Z80 PAGE ;***********************; ; EQUATES ; ;***********************; ;SYSTEM EQUATES BDOS EQU 5 ;BDOS entry point TFCB EQU 5CH ;CP/M DEFAULT FCB CR EQU 0DH ;Carriage return LF EQU 0AH ;Line feed ; prtstr equ 9 ; Print string rdbuf equ 10 ; Read buffer conin equ 1 ; Console input conout equ 2 ; Console output drive equ 25 ; Get present disk drive seldsk equ 14 ; Select disk dskparm equ 31 ; Get disk parm address ;Port offsets for the HDC1001 HDBASE EQU 0E0H ; HDC1001 BASE I/O PORT DATA EQU HDBASE+0 ; Data register ERROR EQU HDBASE+1 ; Error register SECNT EQU HDBASE+2 ; Sector count SECNO EQU HDBASE+3 ; Sector number CYLLO EQU HDBASE+4 ; Cylinder low CYLHI EQU HDBASE+5 ; Cylinder high SDH EQU HDBASE+6 ; Size/Drive/Head COMND EQU HDBASE+7 ; Command register STATUS EQU COMND ; Status register ;Some HDC1001 commands CREAD EQU 20H ; Read sector CWRITE EQU 30H ; Write sector CFORM EQU 50H ; Format track PAGE ;***********************; ; Main program ; ;***********************; START: LD DE,SIGNON ;Send signon message ld C,prtstr call bdos ;Attempt to select drive ld C,DRIVE ; Get current drive call bdos LD (CURDRV),A ; and save it LD A,(TFCB) ; Get selected drive DEC A ; Is it default? JP P,GOTDRV ; No, so we got drive NODRIV: ld DE,nodrv ; Say no drive selected ld C,prtstr call bdos RET ; Exit program GOTDRV: LD E,A ; Else select drive ld C,SELDSK call bdos ld C,DSKPARM ; Get disk parm table call bdos ;Find address to valid hardware description vector LD DE,-4 ; Index just before DPH ADD HL,DE ; to Hardware Description Vect LD A,(HL) ; Should be a word of -1 INC HL AND (HL) INC A JP NZ,BIOERR ; If no -1 then bad BIOS INC HL LD A,(HL) ; Get HDV address INC HL LD H,(HL) LD L,A ; Is it a valid address? OR H JP Z,BIOERR ; No, unsupported device ;Save rest of Hardware Description Vector in memory LD DE,IOPORT ; Copy the rest of the HDV LD BC,9 ; table into local memory ldir ;Calculate old interleave table ;But first clear the table to all FF's DOIT: LD B,64 ; Make table 64 long LD HL,INTTBL ; Index interleave table SETLP: LD (HL),-1 INC HL DEC B JP NZ,SETLP ;Generate 16 bit interleave LD HL,(INTERB) ; Get interleave byte LD H,0 ; and turn it into a word LD (INTER),HL ;Next, calculate interleave table upper limit LD A,(SECS) ; Get number of sectors LD B,A ; Save for later LD E,A ; Offset off base of int tbl LD D,0 LD HL,INTTBL ADD HL,DE LD A,L ; Negate (2s complement) CPL LD L,A LD A,H CPL LD H,A ; This is our upper limit INC HL LD (LIMIT),HL ;Now, negative sector count LD A,B ; Get sectors per track (SECS) CPL ; And twos complement LD L,A LD H,-1 INC HL LD (NSECS),HL CALL CINTER ; Calculate interleave table ;Do a dummy read to insure that the head has beed restored LD A,CREAD OUT (COMND),A RSWAIT: IN A,(STATUS) AND A JP M,RSWAIT ;Figure other needed paramters ; SDH mask LD A,20H ; Get 512 mask LD B,A ; Save for a moment JR NOECC LD A,80H OR B LD B,A NOECC: LD A,(UNIT) ; Get drive number RLCA ; Rotate drive into position RLCA RLCA OR B ; OR in sector size PUSH AF ; Save Size/Drive ;Get bad head, cylinder, and sector ld DE,hdmsg ld C,prtstr call bdos CALL HEXIN ; Get hex input LD A,(HEADS) SUB L ; Make sure it is in range JP C,UNREAS POP AF ; Get Size/Drive OR L ; OR in head OUT (SDH),A ; Send to SDH register ld DE,cylmsg ld C,prtstr call bdos CALL HEXIN LD A,(CYLS) ; Make sure 16 bit cylinder SUB L ; is within range LD A,(CYLS+1) SBC A,H JP C,UNREAS LD A,L ; If within range, send to OUT (CYLLO),A ; WD100X LD A,H OUT (CYLHI),A ld DE,secmsg ld C,prtstr call bdos CALL HEXIN LD A,(SECS) DEC A ; Adjust to be last sector SUB L JP C,UNREAS LD C,L ; Save bad sector ;Change all the bytes in the interleave table to -1 except ;for the bad one. LD A,(SECS) ; Get sector count LD B,A LD HL,INTTBL ; Index interleave table CKSET: LD A,(HL) ; Get sector number CP C ; Same as bad one? JP NZ,NOTBAD OR 40H ; Make bad sector illegal number LD (HL),A JP NOTGUD NOTBAD: LD (HL),-1 ; Mark as good NOTGUD: INC HL DEC B JP NZ,CKSET ;Adjust some interleave table parameters and then generate ;new interleave table LD HL,(LIMIT) ; Make interleave table bigger DEC HL LD (LIMIT),HL LD HL,(NSECS) DEC HL LD (NSECS),HL CALL CINTER ; Calculate interleave table ;Read the entire contents of the selected track ld DE,readng ld C,prtstr call bdos LD HL,TRKBUF ; Index track buffer LD A,(SECS) ; Get sector pointer LD C,A DEC C READLP: LD A,C ; Get sector number OUT (SECNO),A LD A,CREAD ; Get read command OUT (COMND),A LD A,(HSTSIZ) ; Get byte counter/2 LD B,A RWAIT: IN A,(STATUS) ; Wait for it to read AND A JP M,RWAIT ; Ignore all errors except RRA ; Aborted command JP NC,READBF IN A,(ERROR) AND 4 JP NZ,NOTRDY ; If aborted, drive not rdy READBF: IN A,(DATA) ; Read entire sector LD (HL),A INC HL IN A,(DATA) LD (HL),A INC HL DEC B JP NZ,READBF ; Read entire buffer DEC C ; Bump sector counter JP P,READLP ; Continue if still >0 ;Now format the track with the modified interleave ld DE,frmtng ld C,prtstr call bdos LD A,(SECS) ; Get number of sectors INC A ; Add one to it OUT (SECNT),A LD A,CFORM OUT (COMND),A LD DE,INTTBL ; Index interleave table LD A,(HSTSIZ) ; Get transfer size LD B,A DATALP: XOR A ; Get bad block bits OUT (DATA),A LD A,(DE) ; Get sector number OUT (DATA),A INC DE DEC B JP NZ,DATALP ; Continue till sector full FWAIT: IN A,(STATUS) ; Wait for format to happen AND A JP M,FWAIT ; and ignore all errors ;Now write all the original data back to where it logically ;came from ld DE,wrtng ld C,prtstr call bdos LD HL,TRKBUF ; Index track buffer LD A,(SECS) ; Get sector pointer LD C,A DEC C WRITLP: LD A,C ; Get sector number OUT (SECNO),A ; Send to WD100X LD A,CWRITE ; Get read command OUT (COMND),A ; Issue command LD B,0 ; Get byte counter/2, 0 = 256 ld C,data WRITBF: otir ; Send the entire contents otir ; of buffer to WD100X WWAIT: IN A,(STATUS) ; Wait for it to be written AND A JP M,WWAIT RRA ; This time errors CALL C,HERROR ; are important DEC C ; Bump sector counter JP P,WRITLP ; Continue if still >0 ld DE,maped ld C,prtstr call bdos JP RESEL ; Re-select original drive BIOERR: ld DE,bdmsg ld C,prtstr call bdos JP RESEL ; Re-select default drive UNREAS: ld DE,unrs ld C,prtstr call bdos JP RESEL NOTRDY: ld DE,ntrdy ld C,prtstr call bdos ;Re-select formerly current drive RESEL: LD A,(CURDRV) LD E,A LD C,SELDSK CALL BDOS jp 0 ; Exit program PAGE ;***********************; ; Subroutines ; ;***********************; ;This subroutine gives the user a detailed error report HERROR: PUSH HL ; Save important registers PUSH BC ld DE,errmsg ; Send error message ld C,prtstr call bdos IN A,(ERROR) CALL HEXOUT ; Output error ld DE,hderr ; Send head ld C,prtstr call bdos IN A,(SDH) AND 7 CALL OUTCHR ; Print single digit ld DE,cylerr ld C,prtstr call bdos IN A,(CYLHI) ; Report CYLHI first CALL HEXOUT IN A,(CYLLO) ; then CYLLO CALL HEXOUT ld DE,secerr ld C,prtstr call bdos IN A,(SECNO) CALL HEXOUT pop BC ; Restore registers pop HL RET ;This subroutine inputs up to four characters in hex and ;leaves them in HL HEXIN: HEXIN2: LD HL,TBUFF+1 ; Clear input buffer LD B,5 LD (HL),B INC HL CLRLP: LD (HL),0 INC HL DEC B JP NZ,CLRLP ld DE,tbuff ld C,RDBUF ; Get hex from user call bdos LD DE,TBUFF+1 LD A,(DE) ; Make sure at least 1 typed AND A JP Z,UNREAS CP 5 ; and less than 5 JP Z,UNREAS CALL CONHEX ; Do the conversion RET NC ; Carry not set, no errors JP UNREAS ;This subroutine does the hex conversion dirtywork CONHEX: LD HL,0 ; Zero hex accumulator CONLP: INC DE ; Pre-increment text pointer LD A,(DE) ; Get character? AND A ; Last one RET Z ; If so, return to caller CP 'a' ; Fold to upper case JP C,UPPED CP 'z'+1 JP NZ,UPPED SUB 'a'-'A' UPPED: SUB '0' ; Ckeck <0 RET C ADD A,'0'-'G' ; Check >F RET C ADD A,6 JP P,CONCON ADD A,7 RET C CONCON: ADD A,10 LD C,A ; Digit to BC LD B,0 REPT 4 ; Shift left 4 ADD HL,HL ENDM ADD HL,BC ; Add in new digit JP CONLP ;This subroutine prints the contents of A in hex HEXOUT: PUSH AF RRCA ; Get upper digit RRCA RRCA RRCA CALL OUTCHR ; Print upper digit POP AF OUTCHR: AND 0FH ; Mask four bits ADD A,90H DAA ADC A,40H DAA LD E,A ld C,conout ; Write it to console call bdos RET ;This subroutine calculates interleave tables CINTER: LD B,0 ; Clear sector number LD DE,INTTBL ; Index interleave table CHECK: LD HL,(LIMIT) ; Make sure limits not ADD HL,DE ; exceeded JP NC,WITHIN ; We are within limits LD HL,(NSECS) ; Otherwise subtract SECS ADD HL,DE XCHECK: EX DE,HL JP CHECK ; And check limits again WITHIN: LD A,(DE) ; This place taken? AND A JP M,GOTIT ; If not, we can use it INC DE ; Else, check next one JP CHECK GOTIT: LD A,B ; Store sector number LD (DE),A LD HL,(INTER) ; Increment pointer by ADD HL,DE ; interleave INC B ; Increment sector count LD A,(SECS) ; Have we done enough? CP B JP NZ,XCHECK RET PAGE ;***********************; ; Messages ; ;***********************; signon: db 'ADVANCED DIGITAL CORPORATION',CR,LF DB 'HDC1001 Hard Disk Controller Bad Block Mapping Utility',CR,LF,'$' nodrv: db 'No drive selected.$' hdmsg: db 'Head with bad sector? $' cylmsg: db LF,'Cylinder with bad sector? $' secmsg: db LF,'Bad Sector? $' readng: db CR,LF,'Reading track...$' frmtng: db CR,LF,'Formatting track...$' wrtng: db CR,LF,'Writing track...$' maped: db CR,LF,'Map-out completed.$' bdmsg: db 'This drive does not support hardware description vector.$' unrs: db CR,LF,'That''s unreasonable.$' ntrdy: db 'Drive not ready.$' errmsg: db CR,LF,'WD100X Error.$' hderr: db ' on Head $' cylerr: db ', Cylinder $' secerr: db ', Sector $' ;***********************; ; Variables ; ;***********************; ioport: ds 1 ; Base I/O port UNIT: DS 1 ; Physical device unit INTERB: DS 1 ; Current interleave SECS: DS 1 ; Sectors per track CYLS: DS 2 ; Cylinders per drive HEADS: DS 1 ; Heads per drive HSTSIZ: DS 1 ; Host size/128 stprt: ds 1 ; Step rate CURDRV: DS 1 ; Current drive at start INTER: DS 2 ; 16 bit interleave NSECS: DS 2 ; 16 bit negative SECS LIMIT: DS 2 ; Upper memory lmt of table tbuff: db 64 ; Temporary buffer for input ds 6 INTTBL: DS 64 ; Interleave table TRKBUF EQU $ ; Entire track buffer (~8K) END