Contributor: HENNING JORGENSEN 
{$R-,S-,I-,B-,F-,O+}
{---------------------------------------------------------
 BIOS disk I/O routines for floppy drives. Supports DOS
 real mode, DOS protected mode, and Windows. Requires
 TP6, TPW, or BP7.
 All functions are for floppy disks only; no hard drives.
 See the individual types and functions in the interface of
 this unit for more information. See the FMT.PAS sample
 program for an example of formatting disks.
 For status code definitions, see the implementation of
 function GetStatusStr.
 ---------------------------------------------------------
 Based on a unit provided by Henning Jorgensen of Denmark.
 Modified and cleaned up by TurboPower Software for pmode
 and Windows operation.
 TurboPower Software
 P.O. Box 49009
 Colorado Springs, CO 80949-9009
 CompuServe: 76004,2611
 Version 1.0 10/25/93
 Version 1.1 10/29/93
 fix a dumb bug in the MediaArray check
 ---------------------------------------------------------}
unit BDisk;
 {-BIOS disk I/O routines for floppy drives}
interface
const
 MaxRetries : Byte = 3; {Number of automatic retries for
 read, write, verify, format}
type
 DriveNumber = 0..7; {Acceptable floppy drive numbers}
 {Generally, 0 = A, 1 = B}
 DriveType = 0..4; {Floppy drive or disk types}
 {0 = unknown or error
 1 = 360K
 2 = 1.2M
 3 = 720K
 4 = 1.44M}
 VolumeStr = String[11]; {String for volume labels}
 FormatAbortFunc = {Prototype for format abort func}
 function (Track : Byte; {Track number being formatted, 0..MaxTrack}
 MaxTrack : Byte; {Maximum track number for this format}
 Kind : Byte {0 = format beginning}
 {1 = formatting Track}
 {2 = verifying Track}
 {3 = writing boot and FAT}
 {4 = format ending, Track = format status}
 ) : Boolean; {Return True to abort format}
procedure ResetDrive(Drive : DriveNumber);
 {-Reset drive system (function 00ドル). Call after any other
 disk function fails}
function GetDiskStatus : Byte;
 {-Get status of last int 13ドル operation (function 01ドル)}
function GetStatusStr(ErrNum : Byte) : String;
 {-Return message string for any of the status codes used by
 this unit.}
function GetDriveType(Drive : DriveNumber) : DriveType;
 {-Get drive type (function 08ドル). Note that this returns the
 type of the *drive*, not the type of the diskette in it.
 GetDriveType returns 0 for an invalid drive.}
function AllocBuffer(var P : Pointer; Size : Word) : Boolean;
 {-Allocate a buffer useable in real and protected mode.
 Buffers passed to ReadSectors and WriteSectors in pmode
 *MUST* be allocated by using this function. AllocBuffer returns
 False if sufficient memory is not available. P is also set to
 nil in that case.}
procedure FreeBuffer(P : Pointer; Size : Word);
 {-Free buffer allocated by AllocBuffer. Size must match the
 size originally passed to AllocBuffer. FreeBuffer does
 nothing if P is nil.}
function ReadSectors(Drive : DriveNumber;
 Track, Side, SSect, NSect : Byte;
 var Buffer) : Byte;
 {-Read absolute disk sectors (function 02ドル). Track, Side,
 and SSect specify the location of the first sector to
 read. NSect is the number of sectors to read. Buffer
 must be large enough to hold these sectors. ReadSectors
 returns a status code, 0 for success.}
function WriteSectors(Drive : DriveNumber;
 Track, Side, SSect, NSect : Byte;
 var Buffer) : Byte;
 {-Write absolute disk sectors (function 03ドル). Track, Side,
 and SSect specify the location of the first sector to
 write. NSect is the number of sectors to write. Buffer
 must contain all the data to write. WriteSectors
 returns a status code, 0 for success.}
function VerifySectors(Drive : DriveNumber;
 Track, Side, SSect, NSect : Byte) : Byte;
 {-Verify absolute disk sectors (function 04ドル). This
 tests a computed CRC with the CRC stored along with the
 sector. Track, Side, and SSect specify the location of
 the first sector to verify. NSect is the number of
 sectors to verify. VerifySectors returns a status code,
 0 for success. Don't call VerifySectors on PC/XTs and
 PC/ATs with a BIOS from 1985. It will overwrite the
 stack.}
function FormatDisk(Drive : DriveNumber; DType : DriveType;
 Verify : Boolean; MaxBadSects : Byte;
 VLabel : VolumeStr;
 FAF : FormatAbortFunc) : Byte;
 {-Format drive that contains a disk of type DType. If Verify
 is True, each track is verified after it is formatted.
 MaxBadSects specifies the number of sectors that can be
 bad before the format is halted. If VLabel is not an
 empty string, FormatDisk puts the BIOS-level volume
 label onto the diskette. It does *not* add a DOS-level
 volume label. FAF is a user function hook that can be
 used to display status during the format, and to abort
 the format if the user so chooses. Parameters passed to
 this function are described in FormatAbortFunc above.
 FormatDisk also writes a boot sector and empty File
 Allocation Tables for the disk. FormatDisk returns a
 status code, 0 for success.}
function EmptyAbortFunc(Track : Byte; MaxTrack : Byte; Kind : Byte) : Boolean;
 {-Do-nothing abort function for FormatDisk}
 {========================================================================}
implementation
uses
{$IFDEF DPMI}
 WinApi,
 Dos;
 {$DEFINE pmode}
{$ELSE}
{$IFDEF Windows}
 WinApi,
 WinDos;
 {$DEFINE pmode}
{$ELSE}
 Dos;
 {$UNDEF pmode}
{$ENDIF}
{$ENDIF}
{$IFDEF Windows}
type
 Registers = TRegisters;
 DateTime = TDateTime;
{$ENDIF}
type
 DiskRec =
 record
 SSZ : Byte; {Sector size}
 SPT : Byte; {Sectors/track}
 TPD : Byte; {Tracks/disk}
 SPF : Byte; {Sectors/FAT}
 DSC : Byte; {Directory sectors}
 FID : Byte; {Format id for FAT}
 BRD : array[0..13] of Byte; {Variable boot record data}
 end;
 DiskRecs = array[1..4] of DiskRec;
 SectorArray = array[0..511] of Byte;
const
 DData : DiskRecs = {BRD starts at offset 13 of FAT}
 ((SSZ : 02ドル; SPT : 09ドル; TPD : 27ドル; SPF : 02ドル; DSC : 07ドル; FID : $FD; {5.25" - 360K}
 BRD : (02,ドル 01,ドル 00,ドル 02,ドル 70,ドル 00,ドル $D0, 02,ドル $FD, 02,ドル 00,ドル 09,ドル 00,ドル 02ドル)),
 (SSZ : 02ドル; SPT : 0ドルF; TPD : 4ドルF; SPF : 07ドル; DSC : 0ドルE; FID : $F9; {5.25" - 1.2M}
 BRD : (01,ドル 01,ドル 00,ドル 02,ドル $E0, 00,ドル 60,ドル 09,ドル $F9, 07,ドル 00,ドル 0ドルF, 00,ドル 02ドル)),
 (SSZ : 02ドル; SPT : 09ドル; TPD : 4ドルF; SPF : 03ドル; DSC : 07ドル; FID : $F9; {3.50" - 720K}
 BRD : (02,ドル 01,ドル 00,ドル 02,ドル 70,ドル 00,ドル $A0, 05,ドル $F9, 03,ドル 00,ドル 09,ドル 00,ドル 02ドル)),
 (SSZ : 02ドル; SPT : 12ドル; TPD : 4ドルF; SPF : 09ドル; DSC : 0ドルE; FID : $F0; {3.50" - 1.44M}
 BRD : (01,ドル 01,ドル 00,ドル 02,ドル $E0, 00,ドル 40,ドル 0ドルB, $F0, 09,ドル 00,ドル 12,ドル 00,ドル 02ドル)));
 BootRecord : SectorArray = {Standard boot program}
 ($EB, 34,ドル 90,ドル 41,ドル 4ドルD, 53,ドル 54,ドル 20,ドル 33,ドル 2ドルE, 30,ドル 00,ドル 02,ドル 01,ドル 01,ドル 00,ドル 02,ドル $E0, 00,ドル 40,ドル 0ドルB, $F0, 09,ドル 00,ドル
 12,ドル 00,ドル 02,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 12,ドル
 00,ドル 00,ドル 00,ドル 00,ドル 01,ドル 00,ドル $FA, 33,ドル $C0, 8ドルE, $D0, $BC, 00,ドル 7ドルC, 16,ドル 07,ドル $BB, 78,ドル 00,ドル 36,ドル $C5, 37,ドル 1ドルE, 56,ドル
 16,ドル 53,ドル $BF, 2ドルB, 7ドルC, $B9, 0ドルB, 00,ドル $FC, $AC, 26,ドル 80,ドル 3ドルD, 00,ドル 74,ドル 03,ドル 26,ドル 8ドルA, 05,ドル $AA, 8ドルA, $C4, $E2, $F1,
 06,ドル 1ドルF, 89,ドル 47,ドル 02,ドル $C7, 07,ドル 2ドルB, 7ドルC, $FB, $CD, 13,ドル 72,ドル 67,ドル $A0, 10,ドル 7ドルC, 98,ドル $F7, 26,ドル 16,ドル 7ドルC, 03,ドル 06,ドル
 1ドルC, 7ドルC, 03,ドル 06,ドル 0ドルE, 7ドルC, $A3, 3ドルF, 7ドルC, $A3, 37,ドル 7ドルC, $B8, 20,ドル 00,ドル $F7, 26,ドル 11,ドル 7ドルC, 8ドルB, 1ドルE, 0ドルB, 7ドルC, 03,ドル
 $C3, 48,ドル $F7, $F3, 01,ドル 06,ドル 37,ドル 7ドルC, $BB, 00,ドル 05,ドル $A1, 3ドルF, 7ドルC, $E8, 9ドルF, 00,ドル $B8, 01,ドル 02,ドル $E8, $B3, 00,ドル 72,ドル
 19,ドル 8ドルB, $FB, $B9, 0ドルB, 00,ドル $BE, $D6, 7ドルD, $F3, $A6, 75,ドル 0ドルD, 8ドルD, 7ドルF, 20,ドル $BE, $E1, 7ドルD, $B9, 0ドルB, 00,ドル $F3, $A6,
 74,ドル 18,ドル $BE, 77,ドル 7ドルD, $E8, 6ドルA, 00,ドル 32,ドル $E4, $CD, 16,ドル 5ドルE, 1ドルF, 8ドルF, 04,ドル 8ドルF, 44,ドル 02,ドル $CD, 19,ドル $BE, $C0, 7ドルD,
 $EB, $EB, $A1, 1ドルC, 05,ドル 33,ドル $D2, $F7, 36,ドル 0ドルB, 7ドルC, $FE, $C0, $A2, 3ドルC, 7ドルC, $A1, 37,ドル 7ドルC, $A3, 3ドルD, 7ドルC, $BB, 00,ドル
 07,ドル $A1, 37,ドル 7ドルC, $E8, 49,ドル 00,ドル $A1, 18,ドル 7ドルC, 2ドルA, 06,ドル 3ドルB, 7ドルC, 40,ドル 38,ドル 06,ドル 3ドルC, 7ドルC, 73,ドル 03,ドル $A0, 3ドルC, 7ドルC,
 50,ドル $E8, 4ドルE, 00,ドル 58,ドル 72,ドル $C6, 28,ドル 06,ドル 3ドルC, 7ドルC, 74,ドル 0ドルC, 01,ドル 06,ドル 37,ドル 7ドルC, $F7, 26,ドル 0ドルB, 7ドルC, 03,ドル $D8, $EB,
 $D0, 8ドルA, 2ドルE, 15,ドル 7ドルC, 8ドルA, 16,ドル $FD, 7ドルD, 8ドルB, 1ドルE, 3ドルD, 7ドルC, $EA, 00,ドル 00,ドル 70,ドル 00,ドル $AC, 0ドルA, $C0, 74,ドル 22,ドル $B4,
 0ドルE, $BB, 07,ドル 00,ドル $CD, 10,ドル $EB, $F2, 33,ドル $D2, $F7, 36,ドル 18,ドル 7ドルC, $FE, $C2, 88,ドル 16,ドル 3ドルB, 7ドルC, 33,ドル $D2, $F7, 36,ドル
 1ドルA, 7ドルC, 88,ドル 16,ドル 2ドルA, 7ドルC, $A3, 39,ドル 7ドルC, $C3, $B4, 02,ドル 8ドルB, 16,ドル 39,ドル 7ドルC, $B1, 06,ドル $D2, $E6, 0ドルA, 36,ドル 3ドルB, 7ドルC,
 8ドルB, $CA, 86,ドル $E9, 8ドルA, 16,ドル $FD, 7ドルD, 8ドルA, 36,ドル 2ドルA, 7ドルC, $CD, 13,ドル $C3, 0ドルD, 0ドルA, 4ドルE, 6ドルF, 6ドルE, 2ドルD, 53,ドル 79,ドル 73,ドル
 74,ドル 65,ドル 6ドルD, 20,ドル 64,ドル 69,ドル 73,ドル 6ドルB, 20,ドル 6ドルF, 72,ドル 20,ドル 64,ドル 69,ドル 73,ドル 6ドルB, 20,ドル 65,ドル 72,ドル 72,ドル 6ドルF, 72,ドル 0ドルD, 0ドルA,
 52,ドル 65,ドル 70,ドル 6ドルC, 61,ドル 63,ドル 65,ドル 20,ドル 61,ドル 6ドルE, 64,ドル 20,ドル 73,ドル 74,ドル 72,ドル 69,ドル 6ドルB, 65,ドル 20,ドル 61,ドル 6ドルE, 79,ドル 20,ドル 6ドルB,
 65,ドル 79,ドル 20,ドル 77,ドル 68,ドル 65,ドル 6ドルE, 20,ドル 72,ドル 65,ドル 61,ドル 64,ドル 79,ドル 0ドルD, 0ドルA, 00,ドル 0ドルD, 0ドルA, 44,ドル 69,ドル 73,ドル 6ドルB, 20,ドル 42,ドル
 6ドルF, 6ドルF, 74,ドル 20,ドル 66,ドル 61,ドル 69,ドル 6ドルC, 75,ドル 72,ドル 65,ドル 0ドルD, 0ドルA, 00,ドル 49,ドル 4ドルF, 20,ドル 20,ドル 20,ドル 20,ドル 20,ドル 20,ドル 53,ドル 59,ドル
 53,ドル 4ドルD, 53,ドル 44,ドル 4ドルF, 53,ドル 20,ドル 20,ドル 20,ドル 53,ドル 59,ドル 53,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル
 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 00,ドル 55,ドル $AA);
 MediaArray : array[DriveType, 1..2] of Byte =
 ((00,ドル 00ドル), {Unknown disk}
 (01,ドル 02ドル), {360K disk}
 (00,ドル 03ドル), {1.2M disk}
 (00,ドル 04ドル), {720K disk}
 (00,ドル 04ドル)); {1.44M disk}
{$IFDEF pmode}
type
 DPMIRegisters =
 record
 DI : LongInt;
 SI : LongInt;
 BP : LongInt;
 Reserved : LongInt;
 BX : LongInt;
 DX : LongInt;
 CX : LongInt;
 AX : LongInt;
 Flags : Word;
 ES : Word;
 DS : Word;
 FS : Word;
 GS : Word;
 IP : Word;
 CS : Word;
 SP : Word;
 SS : Word;
 end;
 function GetRealSelector(RealPtr : Pointer; Limit : Word) : Word;
 {-Set up a selector to point to RealPtr memory}
 type
 OS =
 record
 O, S : Word;
 end;
 var
 Status : Word;
 Selector : Word;
 Base : LongInt;
 begin
 GetRealSelector := 0;
 Selector := AllocSelector(0);
 if Selector = 0 then
 Exit;
 {Assure a read/write selector}
 Status := ChangeSelector(CSeg, Selector);
 Base := (LongInt(OS(RealPtr).S) shl 4)+LongInt(OS(RealPtr).O);
 if SetSelectorBase(Selector, Base) = 0 then begin
 Selector := FreeSelector(Selector);
 Exit;
 end;
 Status := SetSelectorLimit(Selector, Limit);
 GetRealSelector := Selector;
 end;
 procedure GetRealIntVec(IntNo : Byte; var Vector : Pointer); Assembler;
 asm
 mov ax,0200h
 mov bl,IntNo
 int 31h
 les di,Vector
 mov word ptr es:[di],dx
 mov word ptr es:[di+2],cx
 end;
 function RealIntr(IntNo : Byte; var Regs : DPMIRegisters) : Word; Assembler;
 asm
 xor bx,bx
 mov bl,IntNo
 xor cx,cx {StackWords = 0}
 les di,Regs
 mov ax,0300h
 int 31h
 jc @@ExitPoint
 xor ax,ax
 @@ExitPoint:
 end;
{$ENDIF}
 procedure Int13Call(var Regs : Registers);
 {-Call int 13ドル for real or protected mode}
{$IFDEF pmode}
 var
 Base : LongInt;
 DRegs : DPMIRegisters;
{$ENDIF}
 begin
{$IFDEF pmode}
 {This pmode code is valid only for the AH values used in this unit}
 FillChar(DRegs, SizeOf(DPMIRegisters), 0);
 DRegs.AX := Regs.AX;
 DRegs.BX := Regs.BX;
 DRegs.CX := Regs.CX;
 DRegs.DX := Regs.DX;
 case Regs.AH of
 2, 3, 5 :
 {Calls that use ES as a buffer segment}
 begin
 Base := GetSelectorBase(Regs.ES);
 if (Base <= 0) or (Base> $FFFF0) then begin
 Regs.Flags := 1;
 Regs.AX := 1;
 Exit;
 end;
 DRegs.ES := Base shr 4;
 end;
 end;
 if RealIntr(13,ドル DRegs)  0 then begin
 Regs.Flags := 1;
 Regs.AX := 1;
 end else begin
 Regs.Flags := DRegs.Flags;
 Regs.AX := DRegs.AX;
 Regs.BX := DRegs.BX; {BX is returned by GetDriveType function only}
 end;
{$ELSE}
 Intr(13,ドル Regs);
{$ENDIF}
 end;
 function GetDriveType(Drive : DriveNumber) : DriveType;
 var
 Regs : Registers;
 begin
 Regs.AH := 08ドル;
 Regs.DL := Drive;
 Int13Call(Regs);
 if Regs.AH = 0 then
 GetDriveType := Regs.BL
 else
 GetDriveType := 0;
 end;
 function GetDiskStatus : Byte;
 var
 Regs : Registers;
 begin
 Regs.AH := 01ドル;
 Int13Call(Regs);
 GetDiskStatus := Regs.AL;
 end;
 function GetStatusStr(ErrNum : Byte) : String;
 var
 NumStr : string[3];
 begin
 case ErrNum of
 {Following codes are defined by the floppy BIOS}
 00ドル : GetStatusStr := '';
 01ドル : GetStatusStr := 'Invalid command';
 02ドル : GetStatusStr := 'Address mark not found';
 03ドル : GetStatusStr := 'Disk write protected';
 04ドル : GetStatusStr := 'Sector not found';
 06ドル : GetStatusStr := 'Floppy disk removed';
 08ドル : GetStatusStr := 'DMA overrun';
 09ドル : GetStatusStr := 'DMA crossed 64KB boundary';
 0ドルC : GetStatusStr := 'Media type not found';
 10ドル : GetStatusStr := 'Uncorrectable CRC error';
 20ドル : GetStatusStr := 'Controller failed';
 40ドル : GetStatusStr := 'Seek failed';
 80ドル : GetStatusStr := 'Disk timed out';
 {Following codes are added by this unit}
 $FA : GetStatusStr := 'Format aborted';
 $FB : GetStatusStr := 'Invalid media type';
 $FC : GetStatusStr := 'Too many bad sectors';
 $FD : GetStatusStr := 'Disk bad';
 $FE : GetStatusStr := 'Invalid drive or type';
 $FF : GetStatusStr := 'Insufficient memory';
 else
 Str(ErrNum, NumStr);
 GetStatusStr := 'Unknown error '+NumStr;
 end;
 end;
 procedure ResetDrive(Drive : DriveNumber);
 var
 Regs : Registers;
 begin
 Regs.AH := 00ドル;
 Regs.DL := Drive;
 Int13Call(Regs);
 end;
 function AllocBuffer(var P : Pointer; Size : Word) : Boolean;
 var
 L : LongInt;
 begin
{$IFDEF pmode}
 L := GlobalDosAlloc(Size);
 if L  0 then begin
 P := Ptr(Word(L and $FFFF), 0);
 AllocBuffer := True;
 end else begin
 P := nil;
 AllocBuffer := False
 end;
{$ELSE}
 if MaxAvail>= Size then begin
 GetMem(P, Size);
 AllocBuffer := True;
 end else begin
 P := nil;
 AllocBuffer := False;
 end;
{$ENDIF}
 end;
 procedure FreeBuffer(P : Pointer; Size : Word);
 begin
 if P = nil then
 Exit;
{$IFDEF pmode}
 Size := GlobalDosFree(LongInt(P) shr 16);
{$ELSE}
 FreeMem(P, Size);
{$ENDIF}
 end;
 function CheckParms(DType : DriveType; Drive : DriveNumber) : Boolean;
 {-Make sure drive and type are within range}
 begin
 CheckParms := False;
 if (DType < 1) or (DType> 4) then
 Exit;
 if (Drive> 7) then
 Exit;
 CheckParms := True;
 end;
 function SubfSectors(SubFunc : Byte;
 Drive : DriveNumber;
 Track, Side, SSect, NSect : Byte;
 var Buffer) : Byte;
 {-Code shared by ReadSectors, WriteSectors, VerifySectors, FormatTrack}
 var
 Tries : Byte;
 Done : Boolean;
 Regs : Registers;
 begin
 Tries := 1;
 Done := False;
 repeat
 Regs.AH := SubFunc;
 Regs.AL := NSect;
 Regs.CH := Track;
 Regs.CL := SSect;
 Regs.DH := Side;
 Regs.DL := Drive;
 Regs.ES := Seg(Buffer);
 Regs.BX := Ofs(Buffer);
 Int13Call(Regs);
 if Regs.AH  0 then begin
 ResetDrive(Drive);
 Inc(Tries);
 if Tries> MaxRetries then
 Done := True;
 end else
 Done := True;
 until Done;
 SubfSectors := Regs.AH;
 end;
 function ReadSectors(Drive : DriveNumber;
 Track, Side, SSect, NSect : Byte;
 var Buffer) : Byte;
 begin
 ReadSectors := SubfSectors(02,ドル Drive, Track, Side, SSect, NSect, Buffer);
 end;
 function WriteSectors(Drive : DriveNumber;
 Track, Side, SSect, NSect : Byte;
 var Buffer) : Byte;
 begin
 WriteSectors := SubfSectors(03,ドル Drive, Track, Side, SSect, NSect, Buffer);
 end;
 function VerifySectors(Drive : DriveNumber;
 Track, Side, SSect, NSect : Byte) : Byte;
 var
 Dummy : Byte;
 begin
 VerifySectors := SubfSectors(04,ドル Drive, Track, Side, SSect, NSect, Dummy);
 end;
 function SetDriveTable(DType : DriveType) : Boolean;
 {-Set drive table parameters for formatting}
 var
 P : Pointer;
 DBSeg : Word;
 DBOfs : Word;
 begin
 SetDriveTable := False;
{$IFDEF pmode}
 GetRealIntVec(1ドルE, P);
 DBSeg := GetRealSelector(P, $FFFF);
 if DBSeg = 0 then
 Exit;
 DBOfs := 0;
{$ELSE}
 GetIntVec(1ドルE, P);
 DBSeg := LongInt(P) shr 16;
 DBOfs := LongInt(P) and $FFFF;
{$ENDIF}
 {Set gap length for formatting}
 case DType of
 1 : Mem[DBSeg:DBOfs+7] := 50ドル; {360K}
 2 : Mem[DBSeg:DBOfs+7] := 54ドル; {1.2M}
 3,
 4 : Mem[DBSeg:DBOfs+7] := 6ドルC; {720K or 1.44M}
 end;
 {Set max sectors/track}
 Mem[DBSeg:DBOfs+4] := DData[DType].SPT;
{$IFDEF pmode}
 DBSeg := FreeSelector(DBSeg);
{$ENDIF}
 SetDriveTable := True;
 end;
 function GetMachineID : Byte;
 {-Return machine ID code}
{$IFDEF pmode}
 var
 SegFFFF : Word;
{$ENDIF}
 begin
{$IFDEF pmode}
 SegFFFF := GetRealSelector(Ptr($FFFF, 0000ドル), $FFFF);
 if SegFFFF = 0 then
 GetMachineID := 0
 else begin
 GetMachineID := Mem[SegFFFF:000ドルE];
 SegFFFF := FreeSelector(SegFFFF);
 end;
{$ELSE}
 GetMachineID := Mem[$FFFF:000ドルE];
{$ENDIF}
 end;
 function IsATMachine : Boolean;
 {-Return True if AT or better machine}
 begin
 IsATMachine := False;
 if Lo(DosVersion)>= 3 then
 case GetMachineId of
 $FC, $F8 : {AT or PS/2}
 IsATMachine := True;
 end;
 end;
 function GetChangeLineType(Drive : DriveNumber; var CLT : Byte) : Byte;
 {-Return change line type of drive}
 var
 Regs : Registers;
 begin
 Regs.AH := 15ドル;
 Regs.DL := Drive;
 Int13Call(Regs);
 if (Regs.Flags and FCarry)  0 then begin
 GetChangeLineType := Regs.AH;
 CLT := 0;
 end else begin
 GetChangeLineType := 0;
 CLT := Regs.AH;
 end;
 end;
 function SetFloppyType(Drive : DriveNumber; FType : Byte) : Byte;
 {-Set floppy type for formatting}
 var
 Tries : Byte;
 Done : Boolean;
 Regs : Registers;
 begin
 Tries := 1;
 Done := False;
 repeat
 Regs.AH := 17ドル;
 Regs.AL := FType;
 Regs.DL := Drive;
 Int13Call(Regs);
 if Regs.AH  0 then begin
 ResetDrive(Drive);
 Inc(Tries);
 if Tries> MaxRetries then
 Done := True;
 end else
 Done := True;
 until Done;
 SetFloppyType := Regs.AH;
 end;
 function SetMediaType(Drive : DriveType; TPD : Byte; SPT : Byte) : Byte;
 {-Set media type for formatting}
 var
 Regs : Registers;
 begin
 Regs.AH := 18ドル;
 Regs.DL := Drive;
 Regs.CH := TPD;
 Regs.CL := SPT;
 Int13Call(Regs);
 SetMediaType := Regs.AH;
 end;
 function FormatDisk(Drive : DriveNumber; DType : DriveType;
 Verify : Boolean; MaxBadSects : Byte;
 VLabel : VolumeStr;
 FAF : FormatAbortFunc) : Byte;
 label
 ExitPoint;
 type
 CHRNRec =
 record
 CTrack : Byte; {Track 0..?}
 CSide : Byte; {Side 0..1}
 CSect : Byte; {Sector 1..?}
 CSize : Byte; {Size 0..?}
 end;
 CHRNArray = array[1..18] of CHRNRec;
 FATArray = array[0..4607] of Byte;
 var
 Tries : Byte;
 Track : Byte;
 Side : Byte;
 Sector : Byte;
 RWritten : Byte;
 RTotal : Byte;
 FatNum : Byte;
 BadSects : Byte;
 ChangeLine : Byte;
 DiskType : Byte;
 Status : Byte;
 Done : Boolean;
 Trash : Word;
 DT : DateTime;
 VDate : LongInt;
 Regs : Registers;
 BootPtr : ^SectorArray;
 CHRN : ^CHRNArray;
 FATs : ^FATArray;
 procedure MarkBadSector(Track, Side, Sector : Byte);
 const
 BadMark = $FF7; {Bad cluster mark}
 var
 CNum : Integer; {Cluster number}
 FOfs : Word; {Offset into fat for this cluster}
 FVal : Word; {FAT value for this cluster}
 OFVal : Word; {Old FAT value for this cluster}
 begin
 CNum := (((((Track*2)+Side)*DData[DType].SPT)+Sector-RTotal-2) div
 DData[DType].BRD[0])+2;
 if CNum> 1 then begin
 {Sector is in data space}
 FOfs := (CNum*3) div 2;
 Move(FATs^[FOfs], FVal, 2);
 if Odd(CNum) then
 OFVal := (FVal and (BadMark shl 4))
 else
 OFVal := (FVal and BadMark);
 if OFVal = 0 then begin
 {Not already marked bad, mark it}
 if Odd(CNum) then
 FVal := (FVal or (BadMark shl 4))
 else
 FVal := (FVal or BadMark);
 Move(FVal, FATs^[FOfs], 2);
 {Add to bad sector count}
 Inc(BadSects, DData[DType].BRD[0]);
 end;
 end;
 end;
 begin
 {Validate parameters. Can't do anything unless these are reasonable}
 if not CheckParms(DType, Drive) then
 Exit;
 {Initialize buffer pointers in case of failure}
 FATs := nil;
 CHRN := nil;
 BootPtr := nil;
 {Status proc: starting format}
 if FAF(0, DData[DType].TPD, 0) then begin
 Status := $FA;
 goto ExitPoint;
 end;
 {Error code for invalid drive or media type}
 Status := $FE;
 case GetDriveType(Drive) of
 1 : {360K drive formats only 360K disks}
 if DType  1 then
 goto ExitPoint;
 2 : {1.2M drive formats 360K or 1.2M disk}
 if DType> 2 then
 goto ExitPoint;
 3 : {720K drive formats only 720K disks}
 if DType  3 then
 goto ExitPoint;
 4 : {1.44M drive formats 720K or 1.44M disks}
 if Dtype < 3 then goto ExitPoint; else goto ExitPoint; end; {Error code for out-of-memory or DPMI error} Status := $FF; {Allocate buffers} if not AllocBuffer(Pointer(FATs), SizeOf(FATArray)) then goto ExitPoint; if not AllocBuffer(Pointer(CHRN), SizeOf(CHRNArray)) then goto ExitPoint; if not AllocBuffer(Pointer(BootPtr), SizeOf(BootRecord)) then goto ExitPoint; {Initialize boot record} Move(BootRecord, BootPtr^, SizeOf(BootRecord)); Move(DData[DType].BRD, BootPtr^[13], 14); {Initialize the FAT table} FillChar(FATs^, SizeOf(FATArray), 0); FATs^[0] := DData[DType].FID; FATs^[1] := $FF; FATs^[2] := $FF; {Set drive table parameters by patching drive table in memory} if not SetDriveTable(DType) then goto ExitPoint; {On AT class machines, set format parameters via BIOS} if IsATMachine then begin {Get change line type: 1 -> 360K drive, 2 -> 1.2M or 3.5" drive}
 Status := GetChangeLineType(Drive, ChangeLine);
 if Status  0 then
 goto ExitPoint;
 if (ChangeLine < 1) or (ChangeLine> 2) then begin
 Status := 1;
 goto ExitPoint;
 end;
 {Determine floppy type for SetFloppyType call}
 DiskType := MediaArray[DType, ChangeLine];
 if DiskType = 0 then begin
 Status := $FB;
 goto ExitPoint;
 end;
 {Set floppy type for drive}
 Status := SetFloppyType(Drive, DiskType);
 if Status  0 then
 goto ExitPoint;
 {Set media type for format}
 Status := SetMediaType(Drive, DData[DType].TPD, DData[DType].SPT);
 if Status  0 then
 goto ExitPoint;
 end;
 {Format each sector}
 ResetDrive(Drive);
 BadSects := 0;
 for Track := 0 to DData[DType].TPD do begin
 {Status proc: formatting track}
 if FAF(Track, DData[DType].TPD, 1) then begin
 Status := $FA;
 goto ExitPoint;
 end;
 for Side := 0 to 1 do begin
 {Initialize CHRN for this sector}
 for Sector := 1 to DData[DType].SPT do
 with CHRN^[Sector] do begin
 CTrack := Track;
 CSide := Side;
 CSect := Sector;
 CSize := DData[DType].SSZ;
 end;
 {Format this sector, with retries}
 Status := SubfSectors(05,ドル Drive, Track, Side,
 1, DData[DType].SPT, CHRN^);
 if Status  0 then
 goto ExitPoint;
 end;
 if Verify then begin
 {Status proc: verifying track}
 if FAF(Track, DData[DType].TPD, 2) then begin
 Status := $FA;
 goto ExitPoint;
 end;
 for Side := 0 to 1 do
 {Verify the entire track}
 if VerifySectors(Drive, Track, Side,
 1, DData[DType].SPT)  0 then begin
 if Track = 0 then begin
 {Disk bad}
 Status := $FD;
 goto ExitPoint;
 end;
 for Sector := 1 to DData[DType].SPT do
 if VerifySectors(Drive, Track, Side,
 Sector, 1)  0 then begin
 MarkBadSector(Track, Side, Sector);
 if BadSects> MaxBadSects then begin
 Status := $FC;
 goto ExitPoint;
 end;
 end;
 end;
 end;
 end;
 {Status proc: writing boot and FAT}
 if FAF(0, DData[DType].TPD, 3) then begin
 Status := $FA;
 goto ExitPoint;
 end;
 {Write boot record}
 Status := WriteSectors(Drive, 0, 0, 1, 1, BootPtr^);
 if Status  0 then begin
 Status := $FD;
 goto ExitPoint;
 end;
 {Write FATs and volume label}
 Track := 0;
 Side := 0;
 Sector := 2;
 FatNum := 0;
 RTotal := (2*DData[DType].SPF)+DData[DType].DSC;
 for RWritten := 0 to RTotal-1 do begin
 if Sector> DData[DType].SPT then begin
 Sector := 1;
 Inc(Side);
 end;
 if RWritten < (2*DData[DType].SPF) then begin if FatNum> DData[DType].SPF-1 then
 FatNum := 0;
 end else begin
 FillChar(FATs^, 512, 0);
 if ((VLabel  '') and (RWritten = 2*DData[DType].SPF)) then begin
 {Put in volume label}
 for Trash := 1 to Length(VLabel) do
 VLabel[Trash] := Upcase(VLabel[Trash]);
 while Length(VLabel) < 11 do VLabel := VLabel+' '; Move(VLabel[1], FATs^, 11); FATs^[11] := 8; GetDate(DT.Year, DT.Month, DT.Day, Trash); GetTime(DT.Hour, DT.Min, DT.Sec, Trash); PackTime(DT, VDate); Move(VDate, FATs^[22], 4); end; FatNum := 0; end; if WriteSectors(Drive, Track, Side, Sector, 1, FATs^[FatNum*512])  0 then begin
 Status := $FD;
 goto ExitPoint;
 end;
 Inc(Sector);
 Inc(FatNum);
 end;
 {Success}
 Status := 0;
ExitPoint:
 FreeBuffer(BootPtr, SizeOf(BootRecord));
 FreeBuffer(CHRN, SizeOf(CHRNArray));
 FreeBuffer(FATs, SizeOf(FATArray));
 {Status proc: ending format}
 Done := FAF(Status, DData[DType].TPD, 4);
 FormatDisk := Status;
 end;
 function EmptyAbortFunc(Track, MaxTrack : Byte; Kind : Byte) : Boolean;
 begin
 EmptyAbortFunc := False;
 end;
end.
{ ------------------------------- DEMO PROGRAM -------------------- }
{ ------------------------------- CUT HERE ---------------------}
{$R-,S-,I-}
program Fmt;
 {-Simple formatting program to demonstate DISKB unit}
uses
{$IFDEF Windows}
 WinCrt,
{$ENDIF}
 BDisk;
const
 ESC = #27;
 CR = #13;
type
 CharSet = set of Char;
var
 DLet : Char;
 DTyp : Char;
 Verf : Char;
 GLet : Char;
 DNum : Byte;
 Status : Byte;
 VStr : VolumeStr;
const
 DriveTypeName : array[DriveType] of string[5] =
 ('other', '360K', '1.2M', '720K', '1.44M');
{$IFNDEF Windows}
 function ReadKey : Char; assembler;
 {-Low budget readkey routine}
 asm
 xor ah,ah
 int 16h
 end;
{$ENDIF}
 function GetKey(Prompt : String; OKSet : CharSet) : Char;
 {-Get and return a key in the OKSet}
 var
 Ch : Char;
 begin
 Write(Prompt);
 repeat
 Ch := Upcase(ReadKey);
 if Ch = ESC then begin
 WriteLn;
 Halt;
 end;
 until (Ch in OKSet);
 if Ch  CR then
 Write(Ch);
 WriteLn;
 GetKey := Ch;
 end;
 function AbortFunc(Track, MaxTrack : Byte; Kind : Byte) : Boolean; far;
 {-Display formatting status. Could check for abort here too}
 begin
 case Kind of
 0 : {Format beginning}
 Write('Formatting ');
 1 : {Formatting track}
 Write(^H^H^H^H, ((Track*100) div MaxTrack):3, '%');
 2 : {Verifying track}
 Write(^H, 'V');
 3 : {Writing boot and FAT}
 Write(^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H, 'Writing boot and FAT');
 4 : {Format ending}
 begin
 Write(^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H);
 {Track returns final status code in this case}
 if Track = 0 then
 WriteLn('Formatted successfully')
 else
 WriteLn('Format failed: ', GetStatusStr(Track));
 end;
 end;
 AbortFunc := False;
 end;
begin
 WriteLn('Floppy Formatter:  to exit');
 {Get formatting parameters}
 DLet := GetKey('Drive to format? (A or B): ', ['A'..'B']);
 DTyp := GetKey('Disk type? (1=360K, 2=1.2M, 3=720K, 4=1.44M): ', ['1'..'4']);
 Verf := GetKey('Verify? (Y or N) ', ['N', 'Y']);
 Write('Volume label? ');
 ReadLn(VStr);
 GLet := GetKey('Insert disk and press  ', [#13]);
 {Compute drive number}
 DNum := Byte(DLet)-Byte('A');
 WriteLn('Drive type is ', DriveTypeName[GetDriveType(DNum)]);
 Status := FormatDisk(DNum, {drive number}
 Byte(DTyp)-Byte('0'), {format type}
 (Verf = 'Y'), {verify?}
 10, {max bad sectors}
 VStr, {volume label}
 AbortFunc); {abort function}
 {AbortFunc reports the status}
end.
 

AltStyle によって変換されたページ (->オリジナル) /