home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / dskutl / reform16.arc / REFORMAT.IN5 < prev    next >
Text File  |  1990-03-03  |  42KB  |  1,151 lines

  1. {-------------------- REFORMAT.IN5 -------------------------------------------}
  2. {Disk, FAT, directory, misc. routines}
  3. {v1.6 Rewrote Check_DOS_Version to permit user override
  4.  of precautionary abort for DOS versions above v3.10
  5. }
  6.  
  7. PROCEDURE ResetDisk;
  8.   { flush buffers that weren't written   }
  9.   BEGIN
  10. (*  In Turbo:
  11.     Register.ah := $0D;
  12.     msdos(Register);
  13. *)
  14.     InLine(
  15.   $B4/$0D   {  mov  ah,$0D  ;reset disk}
  16.   /$CD/$21  {  int  $21}
  17. );
  18.   END;   { of ResetDisk }
  19.  
  20.  
  21. PROCEDURE ResetSubdirectory;
  22.   { DOS might not remember }
  23. {
  24.   The directory information DOS provided us with does not contain the
  25.   driveletter, nor starts it with a backslash.
  26. }
  27.   BEGIN
  28.     Move(CurrentDirectory[0], CurrentDirectory[3], 61);
  29.     CurrentDirectory[0] := DriveLetter;
  30.     CurrentDirectory[1] := ':';
  31.     CurrentDirectory[2] := '\';
  32. (* in Turbo:
  33.     Register.ah := $3B;
  34.     Register.ds := Seg(CurrentDirectory);
  35.     Register.dx := Ofs(CurrentDirectory);
  36.     MSDos(Register);
  37. *)
  38.     Inline(
  39.   $B4/$3B                {mov  ah,$3B}
  40.   /$BA/>CURRENTDIRECTORY {mov  dx,>CurrentDirectory}
  41.   /$CD/$21               {int  $21}
  42. );
  43.   END;   { of ResetSubDirectory                 }
  44.  
  45. {----------------------- general disk   I/O routines -------------------------}
  46.  
  47. PROCEDURE Read_Write_Sectors(sectorNumber, numberOfSectors : Word;  {v1.6 INTEGER;}
  48.                              action : Disk_Activity);
  49.   VAR
  50.     intNr : INTEGER;
  51.  
  52.   FUNCTION CarryFlag: BOOLEAN;
  53.     BEGIN
  54.       CarryFlag := Register.flags AND $01 <> 0;
  55.     END;                                 { of CarryFlag }
  56.  
  57.   BEGIN
  58.     WITH Register DO REPEAT
  59.       al := driveNumber;
  60.       cx := numberOfSectors;
  61.       dx := sectorNumber;
  62.       ds := Seg(DTAddress^);
  63.       bx := Ofs(DTAddress^);
  64.       IF action = reading
  65.       THEN BEGIN
  66.         intNr := $25;
  67.         NrStr := 'Error Reading Disk....';
  68.       END
  69.       ELSE BEGIN
  70.         intNr := $26;
  71.         NrStr := 'Error Writing Disk....';
  72.         AlreadyWritten := TRUE;     { set it now because the first write
  73.                                       might succeed partially! }
  74.       END;
  75.       int2526(intNr);                  { 25H = read, 26H = write }
  76.       IF CarryFlag THEN BEGIN
  77.         S40 := 'Enter A (abort), R (retry)';
  78.         IF NOT AlreadyWritten THEN BEGIN
  79.           WriteWarning('No data lost!');
  80.           WriteError(NrStr);
  81.           Legals := 'AR';
  82.         END
  83.         ELSE BEGIN
  84.           WriteError('Probably loss of data!');
  85.           WriteDisaster(NrStr);
  86.           S40 := S40 + ', I(gnore)';
  87.           Legals := 'ARI';
  88.         END;
  89.  
  90.         REPEAT
  91.           Getinput(S40,Instr);
  92.         UNTIL POS(Instr,Legals) <> 0;
  93.  
  94.         IF Instr = 'A' THEN BEGIN
  95.           GotoXY(1,24); WRITELN;  {leave the screen for him or her }
  96.           HALT;
  97.         END
  98.         ELSE BlankFields;
  99.       END;
  100.     UNTIL NOT CarryFlag;
  101.   END;  { of Read_Write_Sectors }
  102.  
  103.  
  104. PROCEDURE ReadCluster(clusterNumber: word);
  105.   VAR   sectorNumber: word;
  106.   BEGIN
  107. (* v1.6
  108.     sectorNumber := W_add(W_mul( clusterSize, clusterNumber - 2 ),
  109.                     firstDataSector);
  110. *)
  111.     sectorNumber := (clusterSize * (clusterNumber-2)) + firstDataSector; {v1.6}
  112.  
  113.     Read_Write_Sectors(sectorNumber, clusterSize,reading);
  114.   END;  { of ReadCluster }
  115.  
  116.  
  117. PROCEDURE WriteCluster(clusterNumber: word);
  118.   VAR   sectorNumber: word;
  119.   BEGIN
  120. (* v1.6
  121.     sectorNumber := W_add(W_mul( clusterSize, clusterNumber - 2 ),
  122.                     firstDataSector);
  123. *)
  124.     sectorNumber := (clusterSize * (clusterNumber-2)) + firstDataSector; {v1.6}
  125.  
  126.     Read_Write_Sectors(sectorNumber, clusterSize, writing);
  127.   END;  { of WriteCluster }
  128.  
  129.  
  130. {----------------------- disk information   routines -------------------------}
  131.  
  132. PROCEDURE ReadBootSector(VAR DTArea: Buffer);
  133. {
  134.   Read the bootsector from disk.
  135. }
  136.   VAR
  137.     BootInfo:          Boot Absolute DTArea;
  138.   BEGIN
  139.     WriteLog('Reading Bootsector.');
  140.     Read_Write_Sectors(0, 1, reading);
  141.     FOR count := 0 TO 7 DO
  142.       OEM[count]       := Bootinfo.OEM[count];
  143.     totalSectors       := Bootinfo.totalSectors;
  144.     trackSize          := Bootinfo.trackSize;
  145.     hiddenSectors      := Bootinfo.hiddenSectors;
  146.   END;  { of ReadBootSector }
  147.  
  148.  
  149. PROCEDURE GetInformation;
  150. {
  151.   Ask DOS for information about the specified drive.
  152.   If we have an error return code from DOS we assume
  153.   that the disk specified was invalid.
  154.   We are using a number of DOS int 21H functions:
  155.  
  156.   1) function 19H : returns current default drive in al: 0 = a, ..
  157.   2) function 32H : ! undocumented function !
  158.                     returns a parameter table which is laid out
  159.                     in the type Parameter_Table.
  160.   3) function 36H : several, but we only use the disk free space
  161.   4) function 47H : current working directory.
  162.  
  163.   the remainder of the information we want to have or need, we find
  164.   in the disks bootrecord and in the FAT (bad space and used space).
  165. }
  166.   VAR
  167.     ValidDrive:   BOOLEAN;
  168.     Parms:        Parms_32;
  169.   BEGIN
  170.     IF Instr = ' '
  171.     THEN GetInput('Enter drive letter',Instr);
  172.     WriteLog('Reading Disk Information');
  173. {
  174.     get current disk: MS-DOS function call 19h
  175.     information is returned in AL: 0 = A, 1 = B, etc.
  176. }
  177.     WITH Register DO BEGIN
  178. (* in Turbo:
  179.       ah := $19;                         { DOS returns the default drive in al  }
  180.       MSDos(Register);                   { as: 0 = A, 1 = B .... }
  181.       defaultDrive := al;
  182. *)
  183.       Inline(
  184.   $B4/$19                {mov  ah,$19}
  185.   /$CD/$21               {int  $21}
  186.   /$A2/>DEFAULTDRIVE     {mov  [>defaultDrive],al}
  187. );
  188.       ValidDrive   := FALSE;
  189.  
  190.       REPEAT                             { keep trying until a good letter }
  191.         IF ORD(Instr) < 64 THEN Instr := CHR($FF);
  192.         DriveLetter := UpCase(Instr);
  193.         driveNumber := ORD(DriveLetter) - 64; { A = 65, so A = 1, B = 2, ...... }
  194.         ah := $36;
  195.         dl := driveNumber;               { must be 0 = default, 1 = A, 2 = B .. }
  196.         MSDos(Register);
  197.         IF ax <> $ffff THEN ValidDrive := TRUE
  198.         ELSE BEGIN
  199.           WriteWarning('Invalid driveletter!');
  200.           GetInput('Enter new letter',Instr);
  201.           WriteWarning(' ');
  202.         END;
  203.       UNTIL ValidDrive;
  204.  
  205.       freeClusters :=          bx;       { we can find that only here }
  206. {
  207.     In case the drive to be reformatted has a current working directory
  208.     DOS may lose track of the current working directory. So we will get
  209.     the current working directory and will tell DOS what it is, when
  210.     we are done.
  211. }
  212.       dl := driveNumber;                 { must be 0 = default, 1 = A, 2 = B .. }
  213.       ds := Seg(CurrentDirectory);
  214.       si := Ofs(CurrentDirectory);
  215.       ah := $47;                         { DOS returns current working directory}
  216.       MSDos(Register);                   { no error, drive checked before }
  217. {
  218.     We now use the undocumented DOS function call 32H. It was described in the
  219.     May 86 PC Tech Journal article "Finding Disk Parameters" by
  220.     Glenn F. Roberts.
  221. }
  222.       dl := driveNumber;                 { must be 0 = default, 1 = A, 2 = B .. }
  223.       ah := $32;
  224.       MSDos(Register);
  225.       Parms                := Ptr(         ds,          bx);
  226.       sectorSize           := Parms^.sectorSize;
  227.       media                := Parms^.MediaDescriptor;
  228.       numberOfHeads        := SUCC(Parms^.numberOfHeads_1);
  229.       reservedSectors      := Parms^.reservedSectors;
  230.       rootDirSize          := Parms^.rootDirSize;
  231.       numberOfFATs         := Parms^.numberOfFATs;
  232.       fatSize              := Parms^.fatSize;
  233.       clusterSize          := SUCC(Parms^.clusterSize_1);
  234.       firstDataSector      := Parms^.firstDataSector;
  235.       firstDirectorySector := Parms^.firstDirectorySector;
  236.       totalDataClusters    := PRED(Parms^.totalDataClusters_1);
  237. (* v1.6
  238.     BigFAT               := W_cmp(totalDataClusters, Gt, 4086);
  239. *)
  240.       BigFAT             := (totalDataClusters > 4086);  {v1.6}
  241.     END;  {of with Register}
  242.  
  243.     IF BigFAT THEN BEGIN
  244.       unused          := $0000;
  245.       reservedMinimum := $FFF0;
  246.       reservedMaximum := $FFF6;
  247.       badCluster      := $FFF7;
  248.       lastMinimum     := $FFF8;
  249.       lastMaximum     := $FFFF;
  250.       lastNormal      := $FFFF;
  251.     END;   { small FAT format was defined already }
  252.  
  253.     deviceDriverAddress  := Parms^.deviceDriverAddress;
  254.     assignedDisk         := Parms^.assignedDisk;
  255.     altAD                := Parms^.altAD;
  256.  
  257.     firstFATSector       := 1;
  258.     Dec(driveNumber);  {INT 25H and 26H expect 0=A, 1=B, etc. v1.6}
  259. {
  260.   The maximum array for the FAT we can allocate is 65521 bytes.
  261.   The length of the FAT is totalDataClusters + 2, so the largest
  262.   FAT we can proces has (65521 div 2 - 2 = 32758) totalDataClusters.
  263.   So we can use the totalDataClusters as an integer, once we have passed
  264.   this test.
  265.   The situation of more than 32758 totalDataClusters will probably never
  266.   occur, because DOS would have problems too with FATs larger than one
  267.   segment of 64Kb.
  268. }
  269. (* v1.6
  270.     IF W_cmp(totalDataClusters, Gt, 32758) THEN BEGIN
  271. *)
  272.     IF totalDataClusters > 32758 THEN BEGIN       {v1.6}
  273.       WriteError('Disk too big for this program...');
  274.       Exeunt(EnterStr);
  275.     END;
  276. {
  277.   Now read the bootrecord, to collect the remaining information.
  278.   Use that procedure, makes things neater.
  279. }
  280.     GetMem(DTAddress, sectorSize);
  281.     ReadBootSector(DTAddress^);
  282.     FreeMem(DTAddress, sectorSize);
  283.   END;  { of GetInformation }
  284.  
  285. {------------------- FAT and file information reading ------------------------}
  286.  
  287. PROCEDURE ReadFat(VAR unscrambledFAT: IntArray; VAR scrambledFAT: Buffer);
  288. {
  289.   Read and unscramble the FAT. Only the first FAT is processed.
  290.   The variable scrambledFAT is really our DTArea.
  291. }
  292.   VAR
  293.     i, temp:     word;  {v1.6 INTEGER;}
  294.   BEGIN
  295.     WriteLog('Reading and unscrambling FAT.');
  296.     Read_Write_Sectors(firstFATSector, fatSize, reading);
  297.     IF BigFAT
  298.     THEN Move(scrambledFAT, unscrambledFAT,
  299. (* v1.6       W_mul(totalDataClusters + 2 , 2)) *)
  300.               (totalDataClusters+2) ShL 1)   {v1.6}
  301.     ELSE FOR i := 0 TO SUCC(totalDataClusters)
  302.     DO BEGIN
  303.       Move( scrambledFAT[3 * i ShR 1], temp, 2);
  304.       IF ODD(i) THEN temp := temp ShR 4 ELSE temp := temp AND $0FFF;
  305.       unscrambledFAT[i] := temp;
  306.     END;
  307.   END;  { of ReadFat }
  308.  
  309.  
  310. PROCEDURE ReadSubdirectory(VAR DTArea:      Buffer;
  311.                            VAR FATarea:     INTArray;
  312.                            VAR SubRoot:     DirectoryPointer;
  313.                            startingCluster: Word;  {v1.6 INTEGER;}
  314.                            hidden:          Word  {v1.6 INTEGER} );
  315. {
  316.   Link subdirectory entries in a list. Build a tree (by calling this
  317.   routine recursively) if a subdirectory is found.
  318. }
  319.   VAR
  320.     clusterNumber,
  321.     dirIndex:      word;  {v1.6 INTEGER;}
  322.     Present:       DirectoryPointer;
  323.     EndSearch:     BOOLEAN;
  324.   BEGIN
  325.     IF hidden <> 0 THEN
  326. (*v1.6 hiddenDirectories := SUCC(hiddenDirectories); *)
  327.       Inc(hiddenDirectories);  {v1.6}
  328. (* v1.6 subdirectories    := SUCC(subdirectories); *)
  329.     Inc(subdirectories);  {v1.6}
  330.     clusterNumber     := startingCluster;
  331.     Present           := NIL;
  332.     SubRoot           := NIL;
  333.     EndSearch         := FALSE;
  334.     REPEAT
  335.       ReadCluster(clusterNumber);
  336.       dirIndex  := 0;
  337.       REPEAT
  338.         IF NOT ( DTArea[dirIndex] IN [NEVERUSED, ERASED] )
  339.         THEN BEGIN
  340.           IF SubRoot = NIL THEN BEGIN
  341.             NEW(SubRoot);
  342.             Present := SubRoot;
  343.           END
  344.           ELSE BEGIN
  345.             NEW(Present^.next);
  346.             Present := Present^.next;
  347.           END;
  348.           Move(DTArea[dirIndex], Present^, 32);
  349.           IF (( Present^.attribute AND SUBDIRECTORY) <> 0 )
  350.           AND ( Present^.EntryName[0] <> '.' )
  351.           THEN BEGIN
  352.             ReadSubdirectory(DTArea, FATarea,Present^.subdirectory,
  353.                              Present^.startingCluster,
  354.                              Present^.attribute AND HIDDENFILE);
  355.             Readcluster(clusterNumber);
  356.           END
  357.           ELSE BEGIN
  358.             Present^.subdirectory := NIL;
  359.             IF Present^.Entryname[0] <> '.'
  360.             THEN BEGIN
  361. (* v1.6
  362.               totalFiles       := SUCC(totalFiles);
  363.               inSubdirectories := SUCC(inSubdirectories);
  364.               IF ( Present^.attribute AND HIDDENFILE ) <> 0
  365.               THEN hiddenFiles := SUCC(hiddenFiles);
  366. *)
  367.               Inc(totalFiles);  {v1.6}
  368.               Inc(inSubdirectories); {v1.6}
  369.               IF ( Present^.attribute AND HIDDENFILE ) <> 0
  370.               THEN Inc(hiddenFiles);  {v1.6}
  371.             END;
  372.           END;
  373.         END
  374.         ELSE IF DTArea[dirIndex] = NEVERUSED
  375.         THEN EndSearch := TRUE;
  376. (* v1.6 dirIndex := dirIndex + 32; *)
  377.         Inc(dirIndex,32);  {v1.6}
  378. (*v1.6UNTIL W_cmp(dirIndex, Ge, W_mul(sectorSize, clusterSize)) *)
  379.       UNTIL (dirIndex >= (sectorSize * clustersize))  {v1.6}
  380.       OR EndSearch;
  381.       clusterNumber :=  FATarea[clusterNumber];
  382. (*v1.6UNTIL  W_cmp(clusterNumber, Ge, reservedMinimum) OR EndSearch; *)
  383.     UNTIL (clusterNumber >= reservedMinimum) OR EndSearch;  {v1.6}
  384.  
  385.     IF Present <> NIL THEN Present^.next := NIL;
  386.   END;  { of ReadSubDirectory }
  387.  
  388.  
  389. PROCEDURE ReadDirectories(VAR DTArea: Buffer);
  390. {
  391.   Read the Rootdirectory and whenever an entry for a subdirectory is
  392.   found call ReadSubdirectory. Link all directory entries dynamically
  393.   in a linked list. This list is actually a tree, because the lists
  394.   for subdirectories are linked to this list.
  395. }
  396.   VAR
  397.     EndSearch:     BOOLEAN;
  398.     sectorNumber,
  399.     dirIndex:      word;  {v1.6 INTEGER;}
  400.     Present:       DirectoryPointer;
  401.   BEGIN
  402.     WriteLog('Reading Directory and Subdirectories.');
  403.     sectorNumber := firstDirectorySector;
  404.     RootDir      := NIL;
  405.     Present      := NIL;
  406.     EndSearch    := FALSE;
  407.     REPEAT
  408.       dirIndex := 0;
  409.       Read_Write_Sectors(sectorNumber, 1, reading);
  410.       REPEAT
  411.         IF NOT ( DTArea[dirIndex] IN [NEVERUSED, ERASED] )
  412.         THEN BEGIN
  413.           IF RootDir = NIL THEN BEGIN
  414.             NEW(RootDir);
  415.             Present := RootDir;
  416.           END
  417.           ELSE BEGIN
  418.             NEW(Present^.next);
  419.             Present := Present^.next;
  420.           END;
  421.           Move(DTArea[dirIndex], Present^, 32);
  422.           IF (( Present^.attribute AND SUBDIRECTORY ) <> 0 )
  423.           AND ( Present^.EntryName[0] <> '.' )
  424.           THEN BEGIN
  425.             ReadSubdirectory(DTArea, OldFATaddress^,
  426.                              Present^.subdirectory,
  427.                              Present^.startingCluster,
  428.                              Present^.attribute AND HIDDENFILE);
  429.             Read_Write_Sectors(sectorNumber, 1, reading);
  430.           END
  431.           ELSE BEGIN
  432.             Present^.subdirectory := NIL;
  433.             IF (( Present^.attribute AND VOLUMELABEL ) = 0 )
  434.             AND ( Present^.Entryname[0] <> '.' )
  435.             THEN BEGIN
  436. (* v1.6
  437.               totalFiles       := SUCC(totalFiles);
  438.               inRootDirectory  := SUCC(inRootDirectory);
  439.               IF ( Present^.attribute AND HIDDENFILE ) <> 0
  440.               THEN hiddenFiles := SUCC(hiddenFiles);
  441. *)
  442.               Inc(totalFiles);  {v1.6}
  443.               Inc(inRootDirectory);  {v1.6}
  444.               IF ( Present^.attribute AND HIDDENFILE ) <> 0
  445.               THEN Inc(hiddenFiles);  {v1.6}
  446.             END
  447.             ELSE IF ( Present^.attribute AND VOLUMELABEL ) <> 0
  448.             THEN BEGIN
  449.               FOR count := 0 TO 10 DO
  450.                 DiskLabel := DiskLabel +
  451.                              Present^.EntryName[count];
  452.               diskCreationDate := Present^.dateLastUpdated;
  453.               diskCreationTime := Present^.timeLastUpdated;
  454.             END;
  455.           END;
  456.         END
  457.         ELSE IF DTArea[dirIndex] = NEVERUSED
  458.         THEN EndSearch := TRUE;
  459. (* v1.6 dirIndex := dirIndex + 32; *)
  460.         Inc(dirIndex,32);  {v1.6}
  461.       UNTIL (dirIndex >= sectorSize ) OR EndSearch;
  462. (*v1.6sectorNumber := SUCC(sectorNumber); *)
  463.       Inc(sectorNumber);  {v1.6}
  464.     UNTIL ( sectorNumber = firstDataSector ) OR EndSearch;
  465.     IF Present <> NIL THEN Present^.next := NIL;
  466.   END;  { of ReadDirectories }
  467.  
  468. {-------------------------- reformatting routines ----------------------------}
  469.  
  470. PROCEDURE RemakeFAT(VAR oldFATarea, newFATarea, permutation: IntArray;
  471.                     Root: DirectoryPointer;
  472.                     parent, thisDir: Word {v1.6 INTEGER} );
  473. {
  474.   This procedure is called recursively.
  475.   From the OldFAT and the directory entries we construct a NewFAT and
  476.   a permutation. The permutation is used by DoIt for moving the
  477.   clusters. This routine is called one extra time for the chain of
  478.   the empty clusters by LinkFreeDataClusters.
  479.   Recursion is used whenever we find an entry for a subdirectory, in
  480.   the following way: first call this routine for the remainder of the
  481.   current directory, then for the subdirectory.
  482.   The function nextFATindex is used to prevent accidental use of clusters
  483.   that were marked as bad or reserved clusters.
  484. }
  485.   FUNCTION nextFATindex: Word;  {v1.6 INTEGER;}
  486.     VAR
  487.       temp: Word;  {v1.6 INTEGER;}
  488.     BEGIN
  489.       temp := SUCC(newFATindex);
  490. (* v1.6
  491.       WHILE W_cmp( oldFATarea[temp], Ge, reservedMinimum ) AND
  492.             W_cmp( oldFATarea[temp], Le, badCluster ) AND
  493.             ( temp <= SUCC(totalDataClusters) )
  494. *)
  495.       WHILE (oldFATarea[temp] >= reservedMinimum)  {v1.6}
  496.         AND (oldFATarea[temp] <= badCluster)
  497.         AND (temp <= SUCC(totalDataClusters))
  498.       DO BEGIN
  499.         newFATarea[temp] := oldFATarea[temp];
  500. (* v1.6
  501.         temp             := SUCC(temp);
  502.         badClusters      := SUCC(badClusters);
  503. *)
  504.         Inc(temp);  {v1.6}
  505.         Inc(badClusters);  {v1.6}
  506.       END;
  507.       nextFATindex := temp;
  508.     END;                               { of nextFATindex }
  509.  
  510.   VAR
  511.     Present: DirectoryPointer;
  512.     Split:   BOOLEAN;
  513.     temp:    Word;  {v1.6 INTEGER;}
  514.   BEGIN   { RemakeFAT }
  515.     IF newFATindex = 1 THEN newFATindex := nextFATindex;
  516.     Present := Root;
  517.     Split   := FALSE;
  518.     WHILE ( Present <> NIL ) AND NOT Split DO BEGIN
  519.       IF (( Present^.attribute AND VOLUMELABEL) = 0 )
  520.       AND ( Present^.startingCluster <> 0 )
  521.       AND ( Present^.Entryname[0] <> '.')
  522.       THEN BEGIN
  523.         IF Present^.subdirectory <> NIL THEN BEGIN
  524.           Split := TRUE;
  525.           RemakeFAT(oldFATarea, newFATarea, permutation,
  526.                     Present^.next, parent, thisDir);
  527.         END;
  528.         oldFATindex                 := Present^.startingCluster;
  529.         Present^.newStartingCluster := newFATindex;
  530.         permutation[newFATindex]    := oldFATindex;
  531. (* v1.6 WHILE W_cmp( oldFATarea[oldFATindex], Lt, lastMinimum ) DO BEGIN *)
  532.         WHILE (oldFATarea[oldFATindex] < lastMinimum) DO BEGIN  {v1.6}
  533.           temp                     := nextFATindex;
  534.           newFATarea[newFATindex]  := temp;
  535.           newFATindex              := temp;
  536.           oldFATindex              := oldFATarea[oldFATindex];
  537.           permutation[newFATindex] := oldFATindex;
  538.         END;
  539.         newFATarea[newFATindex] := lastNormal;
  540.         newFATindex             := nextFATindex;
  541.         IF Split
  542.         THEN RemakeFAT(oldFATarea, newFATarea, permutation,
  543.                        Present^.subdirectory, thisDir,
  544.                        Present^.newStartingCluster);
  545.       END
  546.       ELSE BEGIN
  547.         IF ( Present^.EntryName[0] = '.'  )
  548.         AND ( Present^.EntryName[1] = '.'  )
  549.         THEN Present^.newStartingCluster := parent
  550.         ELSE IF Present^.EntryName[0] = '.'
  551.         THEN Present^.newStartingCluster := thisDir
  552.         ELSE Present^.newStartingCluster := 0;
  553.  
  554.      END;
  555.      Present := Present^.next;
  556.     END;
  557.   END;   { of RemakeFAT }
  558.  
  559.  
  560. PROCEDURE LinkFreeClusters(VAR oldFATarea, newFATarea: IntArray);
  561. {
  562.   Link Free clusters in a chain, pointed to by Empty^.
  563.   Use RemakeFAT to fill permutation, but clean NewFAT after
  564.   this. This procedure will ensure that permutation is a
  565.   proper permutation, without double entries which might
  566.   cause DoIt to loop indefinitely or destroy our disk.
  567. }
  568.   VAR
  569.     count,
  570.     next,
  571.     previous: Word;  {v1.6 INTEGER;}
  572.     Empty:    DirectoryPointer;
  573.   BEGIN
  574.     NEW(Empty);
  575.     Empty^.next            := NIL;
  576.     Empty^.subdirectory    := NIL;
  577.     Empty^.Entryname[0]    := 'X';
  578.     Empty^.attribute       := HIDDENFILE;
  579.     Empty^.startingCluster := 0;
  580.     count                  := 2;
  581.     WHILE ( count <= SUCC(totalDataClusters) )
  582.     AND ( oldFATarea[count] <> 0 )
  583. (*v1.6DO count := SUCC(count);     { find first zero FAT entry } *)
  584.       DO Inc(count);  {find first zero FAT entry  v1.6}
  585.  
  586.     IF count <= SUCC(totalDataClusters)
  587.     THEN BEGIN
  588.       Empty^.startingCluster := count;
  589.       Previous               := count;
  590.       WHILE count < SUCC(totalDataClusters)
  591.       DO BEGIN
  592. (* v1.6 count := SUCC(count); *)
  593.         Inc(count);  {v1.6}
  594.         IF oldFATarea[count] = 0
  595.         THEN BEGIN
  596.           oldFATarea[Previous] := count;
  597.           Previous             := count;
  598.         END;
  599.       END;
  600.       oldFATarea[Previous] := lastNormal;
  601.     END;
  602.  
  603.     IF Empty^.startingCluster <> 0     { plot free clusters in permutation }
  604.     THEN BEGIN
  605.       RemakeFAT(oldFATarea, newFATarea,
  606.                 PermutationAddress^, Empty, 0, 0);
  607.       next := Empty^.newStartingCluster;
  608.       WHILE next <> lastNormal      { clean NewFAT }
  609.       DO BEGIN
  610.         Previous             := next;
  611.         next                 := newFATarea[Previous];
  612.         newFATarea[Previous] := 0;
  613.      END;
  614.    END;
  615.   END;  { of LinkFreeClusters }
  616.  
  617.  
  618. PROCEDURE DoIt(VAR permutation: IntArray; VAR DTArea, SaveArea: Buffer);
  619. {
  620.   DoIt. This routine performs the actual reformatting of the disk.
  621.   The array permutation contains in every location  [i] (starting
  622.   from 2) which cluster has to be moved to cluster location i.
  623.   Because we have a real permutation, this permutation can be
  624.   parsed into a number of cyclical permutations. We start at the
  625.   first cyclic permutation that is not identity. We save the first
  626.   cluster of this cyclical permutation, proceed through the cyclical
  627.   permutation, moving one cluster at a time, until we finish the
  628.   cycle. We than write the saved cluster to disk.
  629. }
  630.   VAR
  631.     prior,
  632.     next,
  633.     lastStart: Word;  {v1.6 INTEGER;}
  634.   BEGIN
  635.     WriteLog('Reformatting......');
  636.     lastStart := 2;
  637.     WHILE lastStart <= SUCC(totalDataClusters)
  638.     DO BEGIN
  639.       IF lastStart = permutation[lastStart]
  640. (*v1.6THEN lastStart := SUCC(lastStart) *)
  641.       THEN Inc(lastStart)  {v1.6}
  642.       ELSE BEGIN
  643.         ReadCluster(lastStart);
  644.         Move(DTArea, SaveArea, sectorSize * clusterSize);
  645.         prior := lastStart;
  646.         next  := permutation[lastStart];
  647.         REPEAT
  648.           ReadCluster(next);
  649.           WriteCluster(prior);
  650. (* v1.6   movedClusters      := SUCC(movedClusters); *)
  651.           Inc(movedClusters);  {v1.6}
  652.           STR(movedClusters:10,NrStr);
  653.           WriteF(movedFieldX, movedFieldY,NrStr);
  654.           permutation[prior] := prior;
  655.           prior              := next;
  656.           next               := permutation[next];
  657.         UNTIL next = lastStart;
  658.         Move(SaveArea, DTArea, sectorSize * clusterSize);
  659.         WriteCluster(prior);
  660. (* v1.6 movedClusters      := SUCC(movedClusters); *)
  661.         Inc(movedClusters);  {v1.6}
  662.         STR(movedClusters:10,NrStr);
  663.         WriteF(movedFieldX, movedFieldY,NrStr);
  664.         permutation[prior] := prior;
  665.       END;
  666.     END;
  667.     WriteLog(' ');
  668.   END;   { of DoIt }
  669.  
  670. {------------------- FAT and file information writing ------------------------}
  671.  
  672. PROCEDURE WriteFat(VAR unscrambledFAT: IntArray; VAR scrambledFAT: Buffer);
  673. {
  674.   Write the FAT back to the disk. The FAT has to be scrambled before
  675.   writing. FAT entries on disk are 12 bits long. If there are
  676.   2 versions of the fat on disk, we write both fats.
  677. }
  678.  
  679.   VAR
  680.     i,
  681.     temp1,
  682.     temp2:  Word;  {v1.6 INTEGER;}
  683.   BEGIN
  684.     WriteLog('Writing FAT.');
  685.     IF BigFAT
  686.     THEN Move(unscrambledFAT, scrambledFAT,
  687. (* v1.6       W_mul(totalDataClusters + 2, 2)) *)
  688.               (totalDataClusters+2) ShL 1 )  {v1.6}
  689.     ELSE FOR i := 0 TO SUCC(totalDataClusters)
  690.     DO BEGIN
  691.       Temp1 := unscrambledFAT[i];
  692.       Move( scrambledFAT[3 * i ShR 1 ], Temp2, 2);
  693.  
  694. {ACHING for inline here.}
  695.  
  696.       IF ODD(i) THEN Temp1 := (Temp2 AND $000F) OR (Temp1 ShL 4)
  697.       ELSE Temp1 := (Temp2 AND $F000) OR Temp1;
  698.       Move( Temp1, scrambledFAT[3 * i ShR 1 ], 2);
  699.     END;
  700.     Read_Write_Sectors(firstFATSector, fatSize, writing);
  701.     IF numberOfFATs = 2
  702.     THEN Read_Write_Sectors(firstFATSector + fatSize, fatSize, writing);
  703.   END;   { of WriteFat }
  704.  
  705.  
  706. PROCEDURE  WriteSubdirectory(VAR DTArea: Buffer; VAR oldFATarea: IntArray;
  707.                              Root: DirectoryPointer;
  708.                              start: Word {v1.6 INTEGER} );
  709. {
  710.   Write subdirectories back to disk. Erased entries are removed
  711.   from the subdirectories. The subdirectories are written to their
  712.   old locations, because DoIt will take care of moving the clusters
  713.   to their new places. No effort is done to truncate a subdirectory
  714.   which would be longer than needed after removal of erased entries.
  715.   We will however set all remaining entries to 'NEVERUSED'.
  716.   This routine is used recursively.
  717. }
  718.   VAR
  719.     start1,
  720.     clusterNumber,
  721.     dirIndex:      Word;  {v1.6 INTEGER;}
  722.     Present:       DirectoryPointer;
  723.   BEGIN
  724.     Present       := Root;
  725.     clusterNumber := Start;
  726.     WHILE Present <> NIL DO BEGIN
  727.       dirIndex := 0;
  728. (*v1.6FillChar(DTArea, W_mul(clusterSize, sectorSize), $00); *)
  729.       FillChar(DTArea, (clusterSize * sectorSize), $00);  {v1.6}
  730.       REPEAT
  731.         start1 := Present^.startingCluster;
  732.         Present^.startingCluster := Present^.newStartingCluster;
  733.         Move(Present^, DTArea[dirIndex], 32);
  734.         IF (( Present^.attribute AND SUBDIRECTORY ) <> 0 )
  735.         AND ( Present^.EntryName[0] <> '.' )
  736.         THEN BEGIN
  737.           WriteCluster(clusterNumber);
  738.           WriteSubdirectory(DTArea, oldFATarea,
  739.                             Present^.subdirectory, start1);
  740.           ReadCluster(clusterNumber);
  741.         END;
  742.         Present  := Present^.next;
  743. (* v1.6 dirIndex := dirIndex + 32; *)
  744.         Inc(dirIndex,32);  {v1.6}
  745. (*v1.6UNTIL W_cmp(dirIndex, Ge, W_mul(clusterSize, sectorSize )) *)
  746.       UNTIL (dirIndex >= (clusterSize * sectorsize)) {v1.6}
  747.       OR ( Present = NIL );
  748.       WriteCluster(clusterNumber);
  749.       clusterNumber := oldFATarea[clusterNumber];
  750.     END;
  751. (*v1.6IF W_cmp( clusterNumber, Lt, lastMinimum ) *)
  752.     IF clusterNumber < lastMinimum  {v1.6}
  753.     THEN BEGIN
  754.       FillChar(DTArea, sectorSize * clusterSize, $00);
  755. (*v1.6WHILE W_cmp( clusterNumber, Lt, lastMinimum ) DO BEGIN *)
  756.       WHILE clusterNumber < lastMinimum DO BEGIN  {v1.6}
  757.         WriteCluster(clusterNumber);
  758.         clusterNumber := oldFATarea[clusterNumber];
  759.      END;
  760.    END;
  761.   END;   { of WriteSubdirectory }
  762.  
  763.  
  764. PROCEDURE WriteDirectories(VAR DTArea: Buffer);
  765. {
  766.   Write rootdirectory back to disk. Erased entries are removed
  767.   from the directory. When we find a subdirectory entry, we first
  768.   process this subdirectory by calling WriteSubdirectories,
  769.   before we proceed with the root. All entries that are no in use
  770.   are set to 'NEVERUSED'.
  771. }
  772.   VAR
  773.     Start,
  774.     sectorNumber,
  775.     dirIndex:      Word;  {v1.6 INTEGER;}
  776.     Present:       DirectoryPointer;
  777.   BEGIN
  778.     WriteLog('Writing new Directory and Subdirectories.');
  779.     sectorNumber := firstDirectorySector;
  780.     Present      := RootDir;
  781.     WHILE Present <> NIL DO BEGIN
  782.       dirIndex := 0;
  783.       FillChar(DTArea, sectorSize, $00);
  784.       REPEAT
  785.         Start := Present^.startingCluster;
  786.         Present^.startingCluster := Present^.newStartingCluster;
  787.         Move(Present^, DTArea[dirIndex], 32);
  788.         IF (( Present^.attribute AND SUBDIRECTORY ) <> 0 )
  789.         AND ( Present^.EntryName[0] <> '.' )
  790.         THEN BEGIN
  791.           Read_Write_Sectors(sectorNumber, 1, writing);
  792.           WriteSubdirectory(DTArea, OldFATaddress^,
  793.                             Present^.subdirectory, Start);
  794.           Read_Write_Sectors(sectorNumber, 1, reading);
  795.         END;
  796.         Present  := Present^.next;
  797. (* v1.6 dirIndex := dirIndex + 32; *)
  798.         Inc(dirIndex,32);  {v1.6}
  799.       UNTIL ( dirIndex >= sectorSize ) OR ( Present = NIL );
  800.       Read_Write_Sectors(sectorNumber, 1, writing);
  801. (*v1.6sectorNumber := SUCC(sectorNumber); *)
  802.       Inc(sectorNumber);  {v1.6}
  803.     END;
  804.     IF sectorNumber < firstDataSector
  805.     THEN BEGIN
  806.       FillChar(DTArea, sectorSize, $00);
  807.       WHILE sectorNumber < firstDataSector DO BEGIN
  808.         Read_Write_Sectors(sectorNumber, 1, writing);
  809. (* v1.6 sectorNumber := SUCC(sectorNumber); *)
  810.         Inc(sectorNumber);  {v1.6}
  811.       END;
  812.     END;
  813.   END;  { of WriteDirectories }
  814.  
  815.  
  816. {----------------------- disk integrety checking routines --------------------}
  817.  
  818. PROCEDURE CheckSubdirectory(VAR FAT: IntArray;
  819.                             Root: DirectoryPointer;
  820.                             parent, thisDir: Word {v1.6 INTEGER} );
  821. {
  822.   This procedure is called recursively.
  823.   The SubDirectories are checked here. No attempt is made
  824.   to correct any errors found. If any errors are found, a message
  825.   is issued and the program stops. The users must first run CHKDSK from
  826.   DOS before we accept the disk.
  827. }
  828.   VAR
  829.     Present: DirectoryPointer;
  830.     prior,
  831.     next:    Word;  {v1.6 INTEGER;}
  832.   BEGIN
  833.     Present := Root;
  834.     WHILE ( Present <> NIL ) AND ( errors = 0 ) DO BEGIN
  835.       IF  (( Present^.attribute AND VOLUMELABEL ) = 0 )
  836.       AND  ( Present^.startingCluster <> 0 )
  837.       AND  ( Present^.Entryname[0] <> '.')
  838.       THEN BEGIN
  839.         next  := Present^.startingCluster;
  840.         count := 0;
  841.         REPEAT;                  { first time never special value !     }
  842.           IF ( next > SUCC(totalDataClusters) )
  843.           OR ( next < 1 )
  844. (* v1.6   THEN errors := SUCC(errors) *)
  845.           THEN Inc(errors)  {v1.6}
  846.           ELSE BEGIN
  847.             prior      := next;
  848.             next       := FAT[prior];
  849.             FAT[prior] := 0;
  850.             IF next <> SUCC(prior)
  851. (* v1.6     THEN count := SUCC(count); *)
  852.             THEN Inc(count);  {v1.6}
  853.           END;
  854. (* v1.6 UNTIL W_cmp( next, Ge, lastMinimum ) OR ( errors <> 0 ); *)
  855.         UNTIL (next >= lastMinimum) OR (errors <> 0); {v1.6}
  856.  
  857.         IF count > 1
  858. (* v1.6 THEN nonContiguousFiles := SUCC(nonContiguousFiles); *)
  859.         THEN Inc(nonContiguousFiles);  {v1.6}
  860.         IF Present^.subdirectory <> NIL
  861.         THEN CheckSubdirectory(FAT, Present^.subdirectory,
  862.                                thisDir, Present^.startingCluster);
  863.       END
  864.       ELSE BEGIN
  865.         IF  ( Present^.EntryName[0] = '.' )
  866.         AND ( Present^.EntryName[1] = '.' )
  867.         THEN IF Present^.startingCluster <> parent
  868.         THEN Inc(errors)  {v1.6 errors := SUCC(errors)}
  869.         ELSE
  870.         ELSE IF Present^.EntryName[0] = '.'
  871.         THEN IF Present^.startingCluster <> thisDir
  872.         THEN Inc(errors)  {v1.6 errors := SUCC(errors)}
  873.         ELSE
  874.         ELSE IF Present^.startingCluster <> 0
  875.         THEN Inc(errors);  {v1.6 errors := SUCC(errors);}
  876.       END;
  877.       Present := Present^.next;
  878.     END;
  879.   END;   { of CheckSubdirectory }
  880.  
  881.  
  882. PROCEDURE CheckDisk(VAR FAT: IntArray; Root: DirectoryPointer);
  883. {
  884.   The FAT and the Directories are checked here. No attempt is made
  885.   to correct any errors found. If any errors are found, a message
  886.   is issued and the program stops. The users must first run CHKDSK from
  887.   DOS before we accept the disk.
  888. }
  889.   BEGIN
  890.     WriteLog('Checking FAT....');
  891.     CheckSubdirectory(FAT, Root, 0, 0);
  892.     FOR count := 2 TO SUCC(totalDataClusters) DO
  893.     IF ( FAT[count] <> 0 ) AND
  894. (* v1.6
  895.            ( W_cmp( FAT[count], Lt, reservedMinimum )
  896.           OR W_cmp( FAT[count], Gt, badCluster) )
  897.          THEN lostClusters := SUCC(lostClusters);
  898. *)
  899.           ( ( FAT[count] < reservedMinimum)  {v1.6}
  900.             OR (FAT[count] > badCluster) )
  901.     THEN Inc(lostClusters);
  902.  
  903.     IF errors <> 0 THEN BEGIN
  904.       WriteError('Crosslinked clusters found. Run CHKDSK first.');
  905.       Exeunt(EnterStr);
  906.     END
  907.     ELSE IF lostClusters <> 0
  908.     THEN BEGIN
  909.       WriteError('Lost clusters found. Run CHKDSK first.');
  910.       Exeunt(EnterStr);
  911.     END;
  912.  END;  { of CheckDisk }
  913.  
  914.  
  915. {--------------------------- miscellaneous routines --------------------------}
  916.  
  917. PROCEDURE CountClustersToMove(VAR permutation: IntArray);
  918.   BEGIN
  919.     FOR count := 2 TO SUCC(totalDataClusters) DO
  920.       IF permutation[count] <> count
  921.       THEN Inc(clustersToMove);  {v1.6 clustersToMove := SUCC(clustersToMove);}
  922.   END;   { of CountClustersToMove }
  923.  
  924.  
  925. PROCEDURE WriteStatistics;
  926.   BEGIN
  927.     usedClusters := totalDataClusters - badClusters - freeClusters;
  928.     toadY := 4;
  929.     IF DiskLabel <> '' THEN
  930.     WriteF(16, 0, 'Volume Label is . . . . . :   ');
  931.     WRITE(DiskLabel);
  932.     WriteF(16, 0, 'Total # of files. . . . . :');
  933.     WRITE(totalFiles:10);
  934.     IF hiddenFiles <> 0
  935.     THEN WRITE(' (hidden:', hiddenFiles:3,')');
  936.     IF subdirectories = 0
  937.     THEN WriteF(16, 0, 'All files in Rootdirectory.')
  938.     ELSE BEGIN
  939.       WriteF(16, 0, '  in Root directory . . . :');
  940.       WRITE(inRootDirectory:10);
  941.       WriteF(16, 0, '  in Subdirectories . . . :');
  942.       WRITE(inSubdirectories:10);
  943.       WriteF(16, 0, '# of subdirectories . . . :');
  944.       WRITE(subdirectories:10);
  945.       IF hiddenDirectories <> 0
  946.       THEN WRITE(' (hidden:', hiddenDirectories:3,')');
  947.     END;
  948.     WriteF(16, 0, '# of noncontiguous files. :');
  949.     WRITE(nonContiguousFiles:10);
  950.     WriteF(16, 0, '# of clusters to be moved :');
  951.     WRITE(clustersToMove:10);
  952.     WriteF(16, 0, '# of clusters moved . . . :');
  953.     WRITE(movedClusters:10);
  954.  
  955.     movedFieldX := 43;
  956.     movedFieldY := PRED(toadY);
  957.  
  958.     STR(totalDataClusters:06, NrStr);
  959.     WriteF(05, SUCC(toadY),
  960.                   'Total space . . :' + NrStr + ' clusters.');
  961.     STR(clusterSize:06, NrStr);
  962.     WriteF(43, PRED(toadY) {0}, 'clusterSize . . :' + NrStr + ' sectors.');
  963.     STR(freeClusters:6,NrStr);
  964.     WriteF(05, 0, 'Free space. . . :' + NrStr + ' clusters.');
  965.     STR(sectorSize:06, NrStr);
  966.     WriteF(43, PRED(toadY) {0}, 'sectorSize. . . :' + NrStr + ' bytes.');
  967.     STR(usedClusters:6,NrStr);
  968.     WriteF(05, 0, 'Used space. . . :' + NrStr + ' clusters.');
  969.     STR(firstDataSector:06, NrStr);
  970.     WriteF(43, PRED(toadY) {0}, 'DOS space . . . :' + NrStr + ' sectors.');
  971.     STR(badClusters:6,NrStr);
  972.     WriteF(05, 0, 'Bad space . . . :' + NrStr + ' clusters.');
  973. (*v1.6toadY := PRED(toadY);              { stay on same line } *)
  974.     Dec(toadY);                        { stay on same line v1.6}
  975.     NrStr := 'Disk type . . . :';
  976.     IF media = FIXEDDISK
  977.     THEN WriteF(43, 0, 'Disk type . . . : Fixed Disk.')
  978.     ELSE IF ( altAD = 0 ) AND ( assignedDisk <> 0 )
  979.     THEN WriteF(43, 0, 'Disk Type . . . : Virtual Disk.')
  980.     ELSE WriteF(43, 0, 'Disk Type . . . : Removable Disk.');
  981.   END;   { of WriteStatistics }
  982.  
  983.  
  984. PROCEDURE InitScreen;
  985.   BEGIN
  986.     NormVideo;
  987.     ClrScr;
  988.     Frame(1);
  989.     Frame(0);
  990.     color := $70;                      {inverse}
  991.     WriteF(0, 2,Header + Version);
  992.     color := LIGHTGRAY;                {normal}
  993.     WriteF(3, inputFieldY ,'User Input Field :');
  994.     WriteF(3,  0 ,'Activity Logging :');
  995.     WriteF(3,  0 ,'Warning  Messages:');
  996.     WriteF(3,  0 ,'Error    Messages:');
  997.     WriteF(3,  0 ,'Disaster Messages:');
  998.   END;   { of InitScreen }
  999.  
  1000.  
  1001. PROCEDURE InitCounters;
  1002.   BEGIN
  1003. (*
  1004.    BigFAT              := FALSE;
  1005.    oldFATindex         := 0;
  1006.    newFATindex         := 1;
  1007.    errors              := 0;
  1008.    lostClusters        := 0;
  1009.    totalFiles          := 0;
  1010.    hiddenFiles         := 0;
  1011.    hiddenDirectories   := 0;
  1012.    inRootDirectory     := 0;
  1013.    inSubdirectories    := 0;
  1014.    nonContiguousFiles  := 0;
  1015.    subdirectories      := 0;
  1016.    movedClusters       := 0;
  1017.    clustersToMove      := 0;
  1018.    count               := 0;
  1019.    AlreadyWritten      := FALSE;
  1020.    DiskLabel           := '';
  1021.    badClusters         := 0;
  1022. *)
  1023. Inline(
  1024.   $31/$C0                {xor  ax,ax  ;get a zero}
  1025.   /$A2/>BIGFAT           {mov  [>BigFAT],al          ;boolean}
  1026.   /$A3/>OLDFATINDEX      {mov  [>oldFATindex],ax}
  1027.   /$A3/>ERRORS           {mov  [>errors],ax}
  1028.   /$A3/>LOSTCLUSTERS     {mov  [>lostClusters],ax}
  1029.   /$A3/>TOTALFILES       {mov  [>totalFiles],ax}
  1030.   /$A3/>HIDDENFILES      {mov  [>hiddenFiles],ax}
  1031.   /$A3/>HIDDENDIRECTORIES{mov  [>hiddenDirectories],ax}
  1032.   /$A3/>INROOTDIRECTORY  {mov  [>inRootDirectory],ax}
  1033.   /$A3/>INSUBDIRECTORIES {mov  [>inSubdirectories],ax}
  1034.   /$A3/>NONCONTIGUOUSFILES{mov  [>nonContiguousFiles],ax}
  1035.   /$A3/>SUBDIRECTORIES   {mov  [>subdirectories],ax}
  1036.   /$A3/>MOVEDCLUSTERS    {mov  [>movedClusters],ax}
  1037.   /$A3/>CLUSTERSTOMOVE   {mov  [>clustersToMove],ax}
  1038.   /$A3/>COUNT            {mov  [>count],ax}
  1039.   /$A2/>ALREADYWRITTEN   {mov  [>AlreadyWritten],al  ;boolean}
  1040.   /$A2/>DISKLABEL        {mov  [>DiskLabel],al       ;string}
  1041.   /$A3/>BADCLUSTERS      {mov  [>badClusters],ax}
  1042.   /$40                   {inc  ax                    ;1}
  1043.   /$A3/>NEWFATINDEX      {mov  [>newFATindex],ax}
  1044. );
  1045.  END;    { of InitCounters }
  1046.  
  1047.  
  1048. PROCEDURE WriteDoc;
  1049.   BEGIN
  1050.     ClrScr;
  1051.     Frame(2);
  1052.     WriteF( 0,  2, Header);
  1053.     WriteF( 0,  0, PDS);
  1054.     WriteF( 0, SUCC(toadY),             { skip a line }
  1055.                    'Makes all files on a floppy or fixed disk ' +
  1056.                    'contiguous again,');
  1057.     WriteF( 0,  0, 'improving  disk  performance  dramatically.');
  1058.     WriteF( 0,  0, 'Either fixed disks or diskettes. Requires DOS 2.0 or up.');
  1059.     WriteF( 0,  0, 'Register at the following address to be on my mailing ' +
  1060.                    'list for updates.');
  1061.     WriteF( 0, SUCC(toadY),            { skip a line  }
  1062.                    'Jos Wennmacker');
  1063.     WriteF( 0,  0, 'Universitair Rekencentrum');
  1064.     WriteF( 0,  0, 'Geert Grooteplein Zuid 41');
  1065.     WriteF( 0,  0, 'NL-6525 GA Nijmegen');
  1066.     WriteF( 0,  0, 'The Netherlands');
  1067.     WriteF( 0, SUCC(toadY),            { skip a line }
  1068.                    'U015415@hnykun22.BITNET');
  1069.     WriteF( 0, SUCC(toadY),            { skip a line }
  1070.                    'Also comments, bugs, etc. are expected at this address.');
  1071.     GetInput('Press enter to see next page',InStr);
  1072.  
  1073.     ClrScr;
  1074.     Frame(2);
  1075.     WriteF( 0,  2, Header);
  1076.     WriteF( 0,  0, PDS);
  1077.     WriteF(32, SUCC(toadY),            { skip a line }
  1078. 'Use: Reformat d:');
  1079.     WriteF(27,  0,
  1080. 'where d: is a driveletter.');
  1081.     WriteF( 0, SUCC(toadY),
  1082. 'This program works for both fixed disks and floppies.');
  1083.     color := color + BLINK;
  1084.     WriteF( 0, toadY + 2,              { skip 2 lines }
  1085. '* WARNING * WARNING * WARNING * WARNING * WARNING * WARNING *');
  1086.     color := color - BLINK;
  1087.     WriteF( 0, toadY + 2,              { skip 2 lines }
  1088. 'NEVER use this program on a disk that contains * PROTECTED * software');
  1089.     WriteF( 0,  0,
  1090. 'You might find these programs turned into an illegal copy');
  1091.     WriteF( 0,  0,
  1092. 'or even end up with a scrambled disk!!!!!!');
  1093.     WriteF( 0,  0,
  1094. 'Always *UNINSTALL* this kind of software before using REFORMAT!!');
  1095.     WriteF( 0,  0,
  1096. 'The program will prompt you to confirm this in case of a fixed disk');
  1097.     WRITELN;
  1098.     WRITELN;
  1099.   END;  { of WriteDoc}
  1100.  
  1101.  
  1102. PROCEDURE Check_DOS_Version;
  1103. {
  1104.   ********************************************************************
  1105.   DOS_Versions  DOS_Versions  DOS_Versions  DOS_Versions  DOS_Versions
  1106.   ********************************************************************
  1107.   Currently DOS 2.00 thru 3.10 are supported.  Mainly  because  of the
  1108.   use of the undocumented DOS  function call 32H, other versions might
  1109.   not be so. DOS function 30H returns the major version number in  al,
  1110.   and the minor version number in ah.  Remember that the  minor number
  1111.   is a two digit number: DOS 3.1 is really DOS 3.10, DOS 3.2 really is
  1112.   3.20
  1113.   ********************************************************************
  1114.   DOS_Versions  DOS_Versions  DOS_Versions  DOS_Versions  DOS_Versions
  1115.   ********************************************************************
  1116.   v1.6  Permitting a user override if higher than DOS 3.10.
  1117.         On your own head be it!
  1118. }
  1119.   VAR  r : REAL;
  1120.   BEGIN
  1121.     WITH Register DO BEGIN
  1122.       ah := $30;
  1123.       MSDos(Register);
  1124.       IF ( al < 2 ) OR (( al = 3 ) AND ( ah > 10 ))
  1125.       THEN BEGIN
  1126. (* v1.6
  1127.         WRITELN('Incorrect DOS version.');
  1128.         HALT;
  1129. *)
  1130.  
  1131.         r := al + (ah / 10);             {convert to a real}
  1132.         STR(r:1:2,NrStr);
  1133.         S40 := 'Enter A (abort), C (continue)';
  1134.         WriteWarning('Untested DOS version!');
  1135.         WriteError('DOS ' + NrStr);      {display DOS version}
  1136.         Legals := 'AC';
  1137.  
  1138.         REPEAT
  1139.           Getinput(S40,Instr);
  1140.         UNTIL POS(Instr,Legals) <> 0;
  1141.  
  1142.         IF Instr = 'A' THEN BEGIN
  1143.           GotoXY(1,24); WRITELN;  {leave the screen for him or her }
  1144.           HALT;
  1145.         END
  1146.         ELSE BlankFields;
  1147.  
  1148.       END;
  1149.     END;
  1150.   END;  { of Check_DOS_Version }
  1151.