home *** CD-ROM | disk | FTP | other *** search
- DECLARE FUNCTION QBFORMAT% (drive%, media%)
- DECLARE FUNCTION FormatDisk% (drive%)
- DECLARE FUNCTION FormatTrack% (drive%, track%, head%)
- DECLARE FUNCTION ValidateDisk% (drive%, media%)
- DECLARE FUNCTION WriteBoot% (drive%)
- DECLARE FUNCTION WriteDir% (drive%)
- DECLARE FUNCTION WriteFAT% (drive%)
- DECLARE FUNCTION WriteSector% (drive%, cyl%, hd%, sec%)
- DECLARE FUNCTION WriteBootSector% (drive%)
- DECLARE SUB ComputeCHS (LogSec%, cyl%, hd%, sec%)
- DECLARE SUB InitFormatParms (media%)
- DECLARE SUB ResetFDC (drive%)
-
- '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
-
-