Dave Navarro, Jr. (dave@powerbasic.com) PB FORMAT FLOPPY DISKS ------------------------------------------------------------------------------ '============================================================================== ' ' PowerBASIC 3.2 source code for formatting floppy disks ' Original by Thaddy De Konig ' Additions and modifications by Dave Navarro, Jr. (dave@powerbasic.com) ' '============================================================================== $DIM ALL DECLARE FUNCTION PBFORMAT(BYVAL drive AS INTEGER, BYVAL media AS INTEGER) AS INTEGER DECLARE FUNCTION ValidateDisk(BYVAL drive AS INTEGER, BYVAL media AS INTEGER) AS INTEGER DECLARE FUNCTION FormatDisk(BYVAL drive AS INTEGER) AS INTEGER DECLARE FUNCTION WriteBoot(BYVAL drive AS INTEGER) AS INTEGER DECLARE FUNCTION WriteFAT(BYVAL drive AS INTEGER) AS INTEGER DECLARE FUNCTION WriteDir(BYVAL drive AS INTEGER) AS INTEGER DECLARE SUB InitFormatParms(BYVAL media AS INTEGER) DECLARE SUB ResetFDC(BYVAL drive AS INTEGER) DECLARE FUNCTION FormatTrack(BYVAL drive AS INTEGER, BYVAL track AS INTEGER, BYVAL head AS INTEGER) AS INTEGER DECLARE FUNCTION WriteBootSector(BYVAL drive AS INTEGER) AS INTEGER DECLARE SUB ComputeCHS( LogSec AS INTEGER, cyl AS INTEGER, hd AS INTEGER, sec AS INTEGER) DECLARE FUNCTION WriteSector( BYVAL drive AS INTEGER, BYVAL cylinder AS INTEGER, BYVAL head AS INTEGER, BYVAL sector AS INTEGER, BYVAL Buffer AS DWORD) AS INTEGER %FLAGS = 0 %AX = 1 %BX = 2 %CX = 3 %DX = 4 %SI = 5 %DI = 6 %BP = 7 %DS = 8 %ES = 9 %F360 = &HFD %F1200 = &HF9 %F720 = - &HF9 ' media byte is NEGATIVE to differ from %F1200 %F1440 = &HF0 %F2880 = - &HF0 ' media byte is negative to differ from %F1440 %RETRIES = 5 '%RETRIES on BIOS error 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 WORD 'bytes/sector SC AS BYTE 'STRING * 1 'sectors/cluster RS AS WORD 'reserved sectors NF AS BYTE 'STRING * 1 'FATs DE AS WORD 'root directory entries TS AS WORD 'total sectors on volume MB AS STRING * 1 'media byte SF AS WORD 'sectors/FAT ST AS WORD 'sectors/track NH AS WORD 'heads HS AS WORD 'hidden sectors END TYPE '27 TYPE BOOTRECtype jmp AS STRING * 3 parms AS INFOtype code AS STRING * 482 END TYPE '512 'INT 1Eh disk parameter table vectors DIM OldDPTseg AS SHARED WORD DIM OldDPToff AS SHARED WORD DIM NewDPTseg AS SHARED WORD DIM NewDPToff AS SHARED WORD 'Number of tracks on media DIM NoTracks AS SHARED INTEGER 'format info for media DIM Info AS SHARED INFOtype 'interface with INTERUPTX routine 'boot record buffer DIM BootRec AS SHARED BOOTRECtype 'sector buffer to write FAT & root directory sectors DIM SectorBuff AS SHARED STRING * 512 DIM Drive AS SHARED INTEGER DIM Media AS SHARED INTEGER DIM verifydisk AS SHARED INTEGER $STATIC 'Allocate address field data to max possible sectors per track DIM AddrField( 1 TO 36 ) AS SHARED ADDRFIELDtype '============================================================================ DIM xerr AS INTEGER DIM errl AS INTEGER DIM errc AS INTEGER '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: drive = 0 media = %F1440 PRINT "Insert 1.44 disk to format in drive A: and" PRINT "Press a key to start PBFORMAT"; SLEEP PRINT PRINT xerr = PBFORMAT( drive, media ) IF xerr THEN errl = xerr \ 256 errc = xerr AND 255 PRINT "*** Error level:"; errl; " code:"; errc ELSE PRINT "done." END IF END '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 ' FUNCTION PBFORMAT(BYVAL drive AS INTEGER, _ BYVAL media AS INTEGER) PUBLIC AS INTEGER DIM xerr AS INTEGER DIM level AS INTEGER xerr = ValidateDisk( drive, media ) IF xerr = 0 THEN xerr = FormatDisk( drive ) ELSE level = 1 END iF IF xerr = 0 THEN PRINT "Writing boot sector..." xerr = WriteBoot( drive ) ELSEIF level = 0 THEN level = 2 END IF IF xerr = 0 THEN PRINT "Creating FAT tables..." xerr = WriteFAT( drive ) ELSEIF level = 0 THEN level = 3 END IF IF xerr = 0 THEN xerr = WriteDir( drive ) ELSEIF level = 0 THEN level = 4 END IF IF xerr THEN IF level = 0 THEN level = 5 END IF END IF 'reset INT 1Eh vector to original,check both for <>0 IF OldDPTseg < > 0 OR OldDPToff < > 0 THEN REG %ax, &H251E REG %ds, OldDPTseg REG %dx, OldDPToff CALL INTERRUPT &H21 END IF FUNCTION = ( level * 256 ) + xerr END FUNCTION 'check if media supported by program, by drive, and if drive is ready FUNCTION ValidateDisk(BYVAL drive AS INTEGER, _ BYVAL media AS INTEGER) PRIVATE AS INTEGER DIM CMOS AS INTEGER DIM checks AS INTEGER DIM zerr AS INTEGER 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 REG %ax, &H351E CALL INTERRUPT &H21 OldDPTseg = REG( %es ) OldDPToff = REG( %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 FOR checks = 1 TO %RETRIES 'zerr=0 no error REG %ax, &H1800 REG %cx,(( NoTracks - 1 ) * 256 ) + Info.ST REG %dx, drive CALL INTERRUPT &H13 zerr = REG( %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 = REG( %es ) NewDPToff = REG( %di ) REG %ax, &H251E REG %ds, NewDPTseg REG %dx, NewDPToff CALL INTERRUPT &H21 ELSE OldDPTseg = 0 OldDPToff = 0 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 REG %ax, &H401 'verify 1 sector from drive REG %cx, &H1 'track=0 sector=1 REG %dx, drive 'head=0 drive=drive FOR checks = 1 TO %RETRIES CALL INTERRUPT &H13 IF REG(( %flags ) AND 1 ) THEN 'bad read zerr = REG( %ax ) 'need to detect an unformatted disk SHIFT RIGHT zerr, 8 '= 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 '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 FUNCTION FormatDisk(BYVAL drive AS INTEGER) AS INTEGER DIM y AS INTEGER DIM x AS INTEGER DIM track AS INTEGER DIM head AS INTEGER DIM xerr AS INTEGER DIM i AS INTEGER y = POS( 0 ) x = CSRLIN LOCATE ,,1 FOR track = 0 TO( NoTracks - 1 ) LOCATE x, y: PRINT "Formatting track:"; track; 'This is where you insert any printed info. 'If you don't want it, remove it...... FOR head = 0 TO( Info.NH - 1 ) FOR i = 1 TO %RETRIES xerr = FormatTrack( drive, track, head ) IF xerr = 0 THEN EXIT FOR END IF NEXT IF xerr THEN PRINT FUNCTION = xerr EXIT FUNCTION END IF NEXT NEXT PRINT FUNCTION = 0 END FUNCTION FUNCTION WriteBoot(BYVAL drive AS INTEGER) AS INTEGER DIM offset AS WORD DIM i AS INTEGER DIM Byte AS INTEGER DIM xerr AS INTEGER 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 FUNCTION = xerr END FUNCTION FUNCTION WriteFAT(BYVAL drive AS INTEGER) AS INTEGER DIM FAT1 AS STRING DIM FAT2 AS STRING DIM i AS INTEGER DIM LogSec AS INTEGER DIM j AS INTEGER DIM k AS INTEGER DIM xerr AS INTEGER DIM cyl AS INTEGER DIM hd AS INTEGER DIM sec AS INTEGER 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 Info.NF FOR j = 1 TO Info.SF IF j = 1 THEN MID$( SectorBuff, 1 ) = FAT1 ELSE MID$( SectorBuff, 1 ) = FAT2 END IF ComputeCHS LogSec, cyl, hd, sec FOR k = 1 TO %RETRIES xerr = WriteSector( drive, cyl, hd, sec, VARPTR32(SectorBuff) ) IF xerr = 0 THEN EXIT FOR NEXT IF xerr THEN FUNCTION = xerr EXIT FUNCTION END IF LogSec = LogSec + 1 NEXT NEXT FUNCTION = 0 END FUNCTION FUNCTION WriteDir(BYVAL drive AS INTEGER) AS INTEGER DIM i AS INTEGER DIM LogSec AS INTEGER DIM k AS INTEGER DIM xerr AS INTEGER DIM cyl AS INTEGER DIM hd AS INTEGER DIM sec AS INTEGER 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 * 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, VARPTR32(SectorBuff) ) IF xerr = 0 THEN EXIT FOR NEXT IF xerr THEN WriteDir = xerr: EXIT FUNCTION LogSec = LogSec + 1 NEXT WriteDir = 0 END FUNCTION 'set up media's format data SUB InitFormatParms(BYVAL media AS INTEGER) Info.OEM = "IBM PB3" 'avoid changing 'IBM' Info.BS = 512 Info.RS = 1 Info.NF = 2 Info.NH = 2 Info.HS = 0 SELECT CASE media CASE %F360 Info.SC = 2 Info.DE = 112 Info.TS = 720 Info.MB = CHR$( %F360 ) Info.SF = 2 Info.ST = 9 CASE %F1200 Info.SC = 1 Info.DE = 224 Info.TS = 2400 Info.MB = CHR$( %F1200 ) Info.SF = 7 Info.ST = 15 CASE %F720 Info.SC = 2 Info.DE = 112 Info.TS = 1440 Info.MB = CHR$( ABS( %F720 )) Info.SF = 3 Info.ST = 9 CASE %F1440 Info.SC = 1 Info.DE = 224 Info.TS = 2880 Info.MB = CHR$( %F1440 ) Info.SF = 9 Info.ST = 18 CASE %F2880 Info.SC = 2 Info.DE = 240 Info.TS = 5760 Info.MB = CHR$( ABS( %F2880 )) Info.SF = 9 Info.ST = 36 CASE ELSE Info.OEM = "" Info.BS = 0 Info.RS = 0 Info.NF = 0 Info.NH = 0 Info.HS = 0 Info.SC = 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 'reset the controller after any BIOS FDC error SUB ResetFDC(BYVAL drive AS INTEGER) ! push DS ! mov AX, 0 ! mov DX, drive ! int &H13 ! pop DS END SUB FUNCTION FormatTrack(BYVAL drive AS INTEGER, _ BYVAL track AS INTEGER, _ BYVAL head AS INTEGER) AS INTEGER DIM sec AS INTEGER DIM cf AS INTEGER DIM e AS WORD '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 REG %ax, &H500 + Info.ST 'format track with sectors/track REG %cx,( track * 256 ) + 1 'track to format,start with sector 1 REG %dx,( head * 256 ) + drive 'head,drive REG %es, VARSEG( AddrField( 1 )) 'point to address field data REG %bx, VARPTR( AddrField( 1 )) CALL INTERRUPT &H13 cf = REG( %flags ) AND 1 'cf=1 if disk error IF cf THEN E = REG( %ax ) SHIFT RIGHT e, 8 'return with status byte FUNCTION = e ResetFDC drive ELSE IF verifydisk THEN REG %ax, &H400 + Info.ST 'ok, verify track integrity- CALL INTERRUPT &H13 '-optional but recommended on format cf = REG( %flags ) AND 1 'cf=1 if disk error IF cf THEN e = REG( %ax ) SHIFT RIGHT e, 8 'return with status byte FUNCTION = e ResetFDC drive END IF END IF END IF FUNCTION = 0 'format ok END FUNCTION 'convert a DOS logical sector to BIOS form SUB ComputeCHS( LogSec AS INTEGER, cyl AS INTEGER, hd AS INTEGER, sec AS INTEGER) DIM CylSec AS INTEGER DIM rm AS INTEGER 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 WriteBootSector(BYVAL drive AS INTEGER) AS INTEGER DIM cf AS INTEGER DIM e AS WORD ! push DS ; save DS for PowerBASIC ! mov AX, &H301 ; write 1 sector ! mov CX, 1 ; track 0,sector 1 ! mov DX, drive ; head 0, drive ! push SS ! pop ES ; ES:BX points to BootRec ! lea BX, BootRec ! int &H13 ; call BIOS ! pop DS ! jnc WriteBootDone ! mov FUNCTION[0], AH ; AH holds error code ResetFDC Drive ' reset floppy controller WriteBootDone: END FUNCTION 'BIOS write a FAT or directory sector FUNCTION WriteSector( BYVAL drive AS INTEGER, _ BYVAL cylinder AS INTEGER, _ BYVAL head AS INTEGER, _ BYVAL sector AS INTEGER, _ BYVAL Buffer AS DWORD ) AS INTEGER ! push DS ! mov AX, &H301 ! mov CL, Byte Ptr sector ! mov CH, Byte Ptr cylinder ! mov DL, Byte Ptr drive ! mov DH, Byte Ptr head ! les BX, Buffer ! int &H13 ! jnc WriteSectorDone ! xchg AL, AH ! xor AH, AH ! mov FUNCTION[0], AX ! push drive ! call WriteSector WriteSectorDone: ! pop DS END FUNCTION 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