'=========================================================================== ' Subject: FORMAT DISKETTE Date: 10-31-90 (00:00) ' Author: Cornel Huth Code: QB, PDS ' Origin: FidoNet QUIK_BAS Echo Packet: DISK.ABC '=========================================================================== 'QBFORMAT.BAS by Cornel Huth 31-Oct-90 'format MS-DOS floppy disks using QB and BIOS 'requires QB.QLB/QB.LIB (specifically INTERRUPTX()) '{5.25-inch media types supported in this code} '{DS9=double-sided 9-sector(360K) DS15=double-sided 15-sector (1.2M)} CONST DS9 = &HFD, DS15 = &HF9 '{3.5-inch media types partially supported in this code} '{DX9=double-sided 9-sector(720K) DX18=double-sided 18-sector (1.44M)} '{*** DX9 media byte is NEGATIVE to differ from DS15 ***} CONST DX9 = -&HF9, DX18 = &HF0 CONST RETRIES = 3 '{retries on BIOS error} TYPE REGtypeX ax AS INTEGER bx AS INTEGER cx AS INTEGER dx AS INTEGER bp AS INTEGER si AS INTEGER di AS INTEGER flags AS INTEGER ds AS INTEGER es AS INTEGER END TYPE '20 TYPE ADDRFIELDtype track AS STRING * 1 head AS STRING * 1 sector AS STRING * 1 bytesec AS STRING * 1 END TYPE '4 TYPE INFOtype OEM AS STRING * 8 'system name BS AS INTEGER 'bytes/sector SC AS STRING * 1 'sectors/cluster RS AS INTEGER 'reserved sectors NF AS STRING * 1 'FATs DE AS INTEGER 'root directory entries TS AS INTEGER 'total sectors on volume MB AS STRING * 1 'media byte SF AS INTEGER 'sectors/FAT ST AS INTEGER 'sectors/track NH AS INTEGER 'heads HS AS INTEGER 'hidden sectors END TYPE '27 TYPE BOOTRECtype jmp AS STRING * 3 parms AS INFOtype code AS STRING * 482 END TYPE '512 '{External routine included with QB 4.0 in QB.LIB & QB.QLB} '{The INTRPT.ASM supplied with QB 4.00 has 2 known bugs, one of} '{which is so bad that this code cannot work properly with it.} '{The INTRPT.ASM supplied with QB 4.00b has 1 known bug, this} '{code will work with it but DOS BIOS INT25/26h calls will not.} '{Later versions may still have that bug!} DECLARE SUB INTERRUPTX (intnum%, ireg AS REGtypeX, oreg AS REGtypeX) DEFINT A-Z '{INT 1Eh disk parameter table vectors} DIM SHARED OldDPTseg, OldDPToff DIM SHARED NewDPTseg, NewDPToff '{Number of tracks on media} DIM SHARED NoTracks '{format info for media} DIM SHARED Info AS INFOtype '{interface with INTERRUPTX routine} DIM SHARED ireg AS REGtypeX, oreg AS REGtypeX '{boot record buffer} DIM SHARED BootRec AS BOOTRECtype '{sector buffer to write FAT & root directory sectors} DIM SHARED SectorBuff AS STRING * 512 REM $STATIC '{Allocate address field data to max possible sectors per track} DIM SHARED AddrField(1 TO 18) AS ADDRFIELDtype BootSector: DATA &HEB,&H3E,&H90,&H20,&H20,&H20,&H20,&H20,&H20,&H20,&H20,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &HA,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H2B,&HC0,&H8E,&HD0,&HBC,&H0,&H7C,&HB8,&HC0,&H7,&H8E,&HD8,&H8E,&HC0,&HBE,&H3 DATA &H0,&HBF,&HAF,&H0,&HB9,&H4,&H0,&HF3,&HA5,&HBE,&HAF,&H0,&HB4,&HE,&H8A,&H4 DATA &HA,&HC0,&H74,&H7,&H56,&HCD,&H10,&H5E,&H46,&HEB,&HF1,&HFC,&HBE,&H7A,&H0,&HBF DATA &H0,&H2,&HB9,&H0,&H2,&HF3,&HA4,&HE9,&H86,&H1,&HB8,&H1,&H2,&H2B,&HDB,&HB9 DATA &H1,&H0,&HBA,&H50,&H0,&HCD,&H13,&H72,&HC,&HBB,&HFE,&H1,&H81,&H3F,&H55,&HAA DATA &H75,&H3,&HE9,&HE5,&HFD,&HBE,&H58,&H2,&HB4,&HE,&H8A,&H4,&HA,&HC0,&H74,&H7 DATA &H56,&HCD,&H10,&H5E,&H46,&HEB,&HF1,&H2B,&HC0,&HCD,&H16,&HCD,&H19,&HCD,&H18,&H20 DATA &H20,&H20,&H20,&H20,&H20,&H20,&H20,&H20,&H6E,&H6F,&H6E,&H2D,&H62,&H6F,&H6F,&H74 DATA &H61,&H62,&H6C,&H65,&H20,&H64,&H69,&H73,&H6B,&H20,&H69,&H6E,&H20,&H41,&H3A,&HD DATA &HA,&H0,&H4E,&H6F,&H20,&H62,&H6F,&H6F,&H74,&H20,&H64,&H69,&H73,&H6B,&H20,&H66 DATA &H6F,&H75,&H6E,&H64,&H2C,&H20,&H72,&H65,&H70,&H6C,&H61,&H63,&H65,&H20,&H61,&H6E DATA &H64,&H20,&H70,&H72,&H65,&H73,&H73,&H20,&H61,&H20,&H6B,&H65,&H79,&H20,&HD,&HA DATA &HD,&HA,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0 DATA &H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H0,&H55,&HAA '============================================================================ '{The following code is a sample run to format a disk} '{All other code can be compiled and put in a library} '{format test drive A: double-sided/9-sector media (360K)} '{...and you thought that this was going to be hard} drive = 0: media = DS9 CLS : DO: LOOP WHILE INKEY$ <> "" INPUT "Insert DS9 disk to format in drive A: and press a key", a$ INPUT "Press a key to start QBFORMAT", a$ PRINT "formatting..." xerr = QBFORMAT(drive, media) IF xerr THEN errl = xerr \ 256 errc = xerr AND 255 PRINT "*** Error level:"; errl; " code:"; errc ELSE PRINT "done." END IF STOP SUB ComputeCHS (LogSec, cyl, hd, sec) '{convert a DOS logical sector to BIOS form} CylSec = Info.ST * Info.NH cyl = LogSec \ CylSec rm = LogSec - (cyl * CylSec) hd = rm \ Info.ST sec = rm - (hd * Info.ST) + 1 END SUB FUNCTION FormatDisk (drive) '{format a track at a time a side at a time} '{any error aborts format,diskette presumed unreliable} '{retry format 1 or 2 more times before trashing the diskette} FOR track = 0 TO (NoTracks - 1) FOR head = 0 TO (Info.NH - 1) FOR i = 1 TO RETRIES xerr = FormatTrack(drive, track, head) IF xerr = 0 THEN EXIT FOR NEXT IF xerr THEN FormatDisk = xerr: EXIT FUNCTION NEXT NEXT FormatDisk = 0 END FUNCTION FUNCTION FormatTrack (drive, track, head) '{Initialize address field for each sector on this track} FOR sec = 1 TO Info.ST AddrField(sec).track = CHR$(track) AddrField(sec).head = CHR$(head) AddrField(sec).sector = CHR$(sec) AddrField(sec).bytesec = CHR$(2) '{bytecode 2 = 512-byte sector} NEXT ireg.ax = &H500 + Info.ST '{format track with sectors/track} ireg.cx = (track * 256) + 1 '{track to format,start with sector 1} ireg.dx = (head * 256) + drive '{head,drive} ireg.es = VARSEG(AddrField(1)) '{point to address field data} ireg.bx = VARPTR(AddrField(1)) INTERRUPTX &H13, ireg, oreg cf = oreg.flags AND 1 '{cf=1 if disk error} IF cf THEN e& = oreg.ax IF e& < 0 THEN e& = e& + 65536 FormatTrack = e& \ 256 '{return with status byte} ResetFDC drive ELSE ireg.ax = &H400 + Info.ST '{ok, verify track integrity-} INTERRUPTX &H13, ireg, oreg '{-optional but recommended on format} cf = oreg.flags AND 1 '{cf=1 if disk error} IF cf THEN e& = oreg.ax IF e& < 0 THEN e& = e& + 65536 FormatTrack = e& \ 256 '{return with status byte} ResetFDC drive ELSE FormatTrack = 0 '{format ok} END IF END IF END FUNCTION SUB InitFormatParms (media) '{set up media's format data} Info.OEM = "IBM 3.1" '{avoid changing 'IBM'} Info.BS = 512 Info.RS = 1 Info.NF = CHR$(2) Info.NH = 2 Info.HS = 0 SELECT CASE media CASE DS9 Info.SC = CHR$(2) Info.DE = 112 Info.TS = 720 Info.MB = CHR$(DS9) Info.SF = 2 Info.ST = 9 CASE DS15 Info.SC = CHR$(1) Info.DE = 224 Info.TS = 2400 Info.MB = CHR$(DS15) Info.SF = 7 Info.ST = 15 CASE DX9 Info.SC = CHR$(2) Info.DE = 112 Info.TS = 720 Info.MB = CHR$(ABS(DX9)) Info.SF = 3 Info.ST = 9 CASE DX18 Info.SC = CHR$(1) Info.DE = 224 Info.TS = 2880 Info.MB = CHR$(DX18) Info.SF = 9 Info.ST = 18 CASE ELSE Info.OEM = "" Info.BS = 0 Info.RS = 0 Info.NF = CHR$(0) Info.NH = 0 Info.HS = 0 Info.SC = CHR$(0) Info.DE = 0 Info.TS = 0 Info.MB = CHR$(0) Info.SF = 0 Info.ST = 0 END SELECT NoTracks = Info.TS \ Info.NH \ Info.ST NewDPTseg = 0 'INT 1Eh vector NewDPToff = 0 'new -> disk parameter table for formatted media OldDPTseg = 0 'original- OldDPToff = 0 '-vector END SUB FUNCTION QBFORMAT (drive, media) '{control routine} xerr = ValidateDisk(drive, media) IF xerr = 0 THEN xerr = FormatDisk(drive) ELSE level = 1 IF xerr = 0 THEN xerr = WriteBoot(drive) ELSE IF level = 0 THEN level = 2 IF xerr = 0 THEN xerr = WriteFAT(drive) ELSE IF level = 0 THEN level = 3 IF xerr = 0 THEN xerr = WriteDir(drive) ELSE IF level = 0 THEN level = 4 IF xerr THEN IF level = 0 THEN level = 5 '{reset INT 1Eh vector to original,check both for <>0} IF OldDPTseg <> 0 OR OldDPToff <> 0 THEN ireg.ax = &H251E ireg.ds = OldDPTseg ireg.dx = OldDPToff INTERRUPTX &H21, ireg, oreg END IF QBFORMAT = (level * 256) + xerr 'sample call 'xerr = QBFORMAT(drive, media) 'IF xerr THEN errl = xerr \ 256: errc = xerr AND 255 'level error codes '0:no error '1:validate error '2:format error '3:boot record write error '4:FAT write error '5:directory write error 'floppy disk error codes (xerr,in decimal): '0:no error '1:invalid function request '2:address mark not found '3:write protected '4:sector not found '6:diskette changed '8:DMA overrun '9:DMA boundary error '12:media type not available '16:bad CRC '32:diskette controller failed '64:seek failed '128:time-out/drive not ready END FUNCTION SUB ResetFDC (drive) '{reset the controller after any BIOS FDC error} tax = ireg.ax tdx = ireg.dx ireg.ax = 0 ireg.dx = drive INTERRUPTX &H13, ireg, oreg ireg.ax = tax ireg.dx = tdx END SUB FUNCTION ValidateDisk (drive, media) '{check if media supported by program,by drive,and if drive is ready} InitFormatParms media IF Info.ST THEN '{check to see if there is CMOS RAM (means we have an AT BIOS)} OUT &H70, &H10 CMOS = (INP(&H71) <> &HFF) ResetFDC drive IF CMOS THEN '{get original INT 1Eh vector to disk parameter table} ireg.ax = &H351E INTERRUPTX &H21, ireg, oreg OldDPTseg = oreg.es OldDPToff = oreg.bx '{set media type for format on AT BIOS/multi-media drive} '{let BIOS determine if drive can format media with a DPT in BIOS ROM} ireg.ax = &H1800 ireg.cx = ((NoTracks - 1) * 256) + Info.ST ireg.dx = drive FOR checks = 1 TO RETRIES '{zerr=0 no error} INTERRUPTX &H13, ireg, oreg '{zerr=1 drive invalid} zerr = oreg.ax \ 256 '{zerr=&H0C unknown media/maybe invalid CMOS} IF zerr THEN ResetFDC drive ELSE EXIT FOR NEXT '{set INT 1Eh vector to this media's disk parameter table in BIOS ROM} IF zerr = 0 THEN NewDPTseg = oreg.es NewDPToff = oreg.di ireg.ax = &H251E ireg.ds = NewDPTseg ireg.dx = NewDPToff INTERRUPTX &H21, ireg, oreg ELSE OldDPTseg = 0 OldDPToff = 0 END IF ELSE '{pre-AT BIOS} '{much more work involved} '{if you want more than DS9 support, you need to add it} DEF SEG = 0: Equip = PEEK(&H410): DEF SEG IF Equip AND 1 THEN MaxDrive = ((Equip AND &HC0) \ 64) '{MaxDrive=0=1drive, 1=2} IF (drive >= 0) AND (drive <= MaxDrive) THEN 'get diskette parameter table vector ireg.ax = &H351E INTERRUPTX &H21, ireg, oreg DEF SEG = oreg.es: MaxSectors = PEEK(oreg.bx + 4): DEF SEG IF MaxSectors = 9 THEN SELECT CASE media CASE DS9 '{supported media} CASE ELSE zerr = &HC '{invalid media} END SELECT '{any other max sectors not supported here} ELSE SELECT CASE media CASE ELSE zerr = &HC '{invalid media} END SELECT END IF ELSE zerr = 1 '{invalid drive} END IF ELSE zerr = 1 '{no drives to format} END IF END IF ELSE zerr = &HC '{media not supported by program} END IF '{physical check for disk in the drive} IF zerr = 0 THEN ireg.ax = &H401 '{verify 1 sector from drive} ireg.cx = &H1 '{track=0 sector=1} ireg.dx = drive '{head=0 drive=drive} FOR checks = 1 TO RETRIES INTERRUPTX &H13, ireg, oreg IF oreg.flags AND 1 THEN '{bad read} e& = oreg.ax '{need to detect an unformatted disk} IF e& < 0 THEN e& = e& + 65536 zerr = e& \ 256 ResetFDC drive ELSE zerr = 0 '{good read,already formatted disk in drive} EXIT FOR END IF NEXT '{zerr may be any of the BIOS diskette error codes if non-zero here} '{address mark not found(2)=unformatted disk in drive} '{sector not found(4)=wacko disk but okay to proceed} IF zerr = 2 OR zerr = 4 THEN zerr = 0 END IF ValidateDisk = zerr END FUNCTION FUNCTION WriteBoot (drive) RESTORE BootSector '{read default boot record data} DEF SEG = VARSEG(BootRec): offset = VARPTR(BootRec) FOR i = 0 TO 511: READ byte: POKE offset + i, byte: NEXT: DEF SEG '{update OEM name and BIOS parameter block} BootRec.parms = Info '{write the boot record} FOR i = 1 TO RETRIES xerr = WriteBootSector(drive) IF xerr = 0 THEN EXIT FOR NEXT WriteBoot = xerr END FUNCTION FUNCTION WriteBootSector (drive) ireg.ax = &H300 + 1 '{write 1 sector} ireg.cx = 1 '{track 0,sector 1} ireg.dx = drive '{head 0,drive} ireg.es = VARSEG(BootRec) '{point to boot record data} ireg.bx = VARPTR(BootRec) INTERRUPTX &H13, ireg, oreg cf = oreg.flags AND 1 '{cf=1 if disk error} IF cf THEN e& = oreg.ax IF e& < 0 THEN e& = e& + 65536 WriteBootSector = e& \ 256 '{return with status byte} ResetFDC drive ELSE WriteBootSector = 0 '{ok} END IF END FUNCTION FUNCTION WriteDir (drive) MID$(SectorBuff, 1, 1) = CHR$(0) FOR i = 2 TO 512 IF (i - 1) MOD 32 = 0 THEN MID$(SectorBuff, i, 1) = CHR$(0) ELSE MID$(SectorBuff, i, 1) = CHR$(&HF6) END IF NEXT LogSec = Info.RS + Info.HS + (Info.SF * ASC(Info.NF)) 'first logical dir sector FOR i = 1 TO (Info.DE \ 16) 'sectors needed for root directory ComputeCHS LogSec, cyl, hd, sec FOR k = 1 TO RETRIES xerr = WriteSector(drive, cyl, hd, sec) IF xerr = 0 THEN EXIT FOR NEXT IF xerr THEN WriteDir = xerr: EXIT FUNCTION LogSec = LogSec + 1 NEXT WriteDir = 0 END FUNCTION FUNCTION WriteFAT (drive) '{do the hussle} FAT1$ = Info.MB + CHR$(255) + CHR$(255) FAT2$ = CHR$(0) + CHR$(0) + CHR$(0) FOR i = 1 TO 512: MID$(SectorBuff, i, 1) = CHR$(0): NEXT LogSec = Info.RS + Info.HS 'first logical FAT sector FOR i = 1 TO ASC(Info.NF) FOR j = 1 TO Info.SF IF j = 1 THEN MID$(SectorBuff, 1) = FAT1$ ELSE MID$(SectorBuff, 1) = FAT2$ ComputeCHS LogSec, cyl, hd, sec FOR k = 1 TO RETRIES xerr = WriteSector(drive, cyl, hd, sec) IF xerr = 0 THEN EXIT FOR NEXT IF xerr THEN WriteFAT = xerr: EXIT FUNCTION LogSec = LogSec + 1 NEXT NEXT WriteFAT = 0 END FUNCTION FUNCTION WriteSector (drive, cyl, hd, sec) '{BIOS write a FAT or directory sector} ireg.ax = &H300 + 1 ireg.cx = (cyl * 256) + sec ireg.dx = (hd * 256) + drive ireg.es = VARSEG(SectorBuff) ireg.bx = VARPTR(SectorBuff) INTERRUPTX &H13, ireg, oreg cf = oreg.flags AND 1 IF cf THEN e& = oreg.ax IF e& < 0 THEN e& = e& + 65536 WriteSector = e& \ 256 ResetFDC drive ELSE WriteSector = 0 END IF END FUNCTION