home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / FILECOPY.ZIP / FCOPY.PAS
Encoding:
Pascal/Delphi Source File  |  1993-01-04  |  19.4 KB  |  671 lines

  1. {$C-}
  2. {
  3. reads a master DSDD disk into memory.
  4. writes it to drives A and B in alternation without rereading the master.
  5. requires 2 floppy drives (could be modified for a single one).
  6. works for DSDD DOS 2.0+ 360K floppies only.
  7. works for PC-DOS only.
  8.  
  9. written 7/28/86, Kim Kokkonen, TurboPower Software
  10. Compuserve 72457,2131.
  11.  
  12. Altered by Bill Bliss to format disks, too (2/87).
  13. Altered by Bill Bliss and Mark Solinski to handle 64K DMA boundary conditions
  14.   that occur on some cheap IBM clones (4/20/87).
  15.  
  16. Released to the public domain.
  17. }
  18.  
  19. program fcopy;
  20.   { fast DSDD disk copier }
  21.  
  22. { Demonstrates formatting a floppy disk from Turbo Pascal.
  23.   This version only formats DSDD 9 sector floppies (360KB).
  24.   Works with DOS 2.0 or 3.0 in either 360K or 1.2M drives.
  25.   Does not support a /S option.
  26.  
  27.   If DoVerify is false, obtain a 2x speedup vs. DOS format.
  28.  
  29.   Requires Turbo version 3.0 to compile.
  30.  
  31.   Requires a cloning procedure after being compiled to a .COM file.
  32.   The cloning procedure copies the boot sector of an already-formatted
  33.   floppy into the program, where it can be used thereafter. To clone,
  34.   call the program as follows:
  35.  
  36.   ProgramName @ <enter>
  37.  
  38.   Written 10/26/85. Kim Kokkonen, TurboPower Software.
  39.   Compuserve 72457,2131.
  40.  
  41.   Altered slightly by Bill Bliss, The Whitewater Group,
  42.   so that it could be used as an include file by FCOPY.PAS.
  43. }
  44.  
  45. const
  46.   ProgramName = 'FCOPY.COM';
  47.  
  48.   TYPE
  49.     {holds sector data during sector setup after formatting}
  50.     SectorBuffer = ARRAY[1..512] OF Char;
  51.     {same size as sector buffer but easily initialized in code segment}
  52.     FakeSectorBuffer = RECORD
  53.                          l, h : STRING[255];
  54.                        END;
  55.     FormatRecord = RECORD
  56.                      cyl, hed, rec, num : Byte;
  57.                    END;
  58.     FormatArray = ARRAY[1..18] OF FormatRecord;
  59.     DiskBaseRec = RECORD
  60.                     unk1, unk2, mtr, bps, eot, gpl,
  61.                     dtl, glf, fbf, hst, mst : Byte;
  62.                   END;
  63.     DiskBasePtr = ^DiskBaseRec;
  64.     registers = RECORD
  65.                   CASE Integer OF
  66.                     1 : (ax, bx, cx, dx, bp, si, di, ds, es, flags : Integer);
  67.                     2 : (al, ah, bl, bh, cl, ch, dl, dh : Byte);
  68.                 END;
  69.     FATtable = ARRAY[0..1023] OF Byte;
  70.  
  71.   CONST
  72.     BootRecord : FakeSectorBuffer = {fill in with bootrecord}
  73.     (l : ''; h : '');
  74.  
  75.   VAR
  76.     reg : registers;
  77.     SB : SectorBuffer;        {will fill in with dir sectors}
  78.     BR : SectorBuffer ABSOLUTE BootRecord;
  79.     VB : ARRAY[1..9] OF SectorBuffer; {will use for fast verify}
  80.     FMT : FormatArray;
  81.     FAT : FATtable;
  82.     Drive, dType : Byte;
  83.     ch : Char;
  84.     BiosDiskBase : DiskBasePtr ABSOLUTE 0 : $78;
  85.     OldDiskBase : DiskBasePtr;
  86.     error : Integer;
  87.  
  88.   PROCEDURE BIOSreadSectors(funct, drive : Byte;
  89.                             sector, track, head : Integer;
  90.                             sects : Integer;
  91.                             VAR buffer;
  92.                             VAR error : Integer);
  93.       { execute int 13 to read disk or verify via BIOS at low level}
  94.     BEGIN
  95.       reg.ax := (funct SHL 8) OR sects;
  96.       reg.dl := drive;
  97.       reg.dh := head;
  98.       reg.ch := track AND 255;
  99.       reg.cl := (sector AND 63) OR ((track SHR 8) SHL 6);
  100.       reg.es := Seg(buffer);
  101.       reg.bx := Ofs(buffer);
  102.       Intr($13, reg);
  103.       IF Odd(reg.flags AND 1) THEN
  104.         error := reg.ax SHR 8
  105.       ELSE
  106.         error := 0;
  107.     END {biosreadsectors} ;
  108.  
  109.   PROCEDURE BIOSwriteSectors(drive : Byte;
  110.                              sector, track, head : Integer;
  111.                              sects : Integer;
  112.                              VAR buffer;
  113.                              VAR error : Integer);
  114.       { execute int 13 to write disk via BIOS at low level}
  115.     BEGIN
  116.       reg.ax := $300 OR sects;
  117.       reg.dl := drive;
  118.       reg.dh := head;
  119.       reg.ch := track AND 255;
  120.       reg.cl := (sector AND 63) OR ((track SHR 8) SHL 6);
  121.       reg.es := Seg(buffer);
  122.       reg.bx := Ofs(buffer);
  123.       Intr($13, reg);
  124.       IF Odd(reg.flags AND 1) THEN
  125.         error := reg.ax SHR 8
  126.       ELSE
  127.         error := 0;
  128.     END {BIOSWriteSectors} ;
  129.  
  130.  
  131.   PROCEDURE InitBoot;
  132.       { self-customize this program to hold the boot record}
  133.     VAR
  134.       ch : Char;
  135.       error : Integer;
  136.       f : FILE;
  137.       tries : Byte;
  138.  
  139.     FUNCTION CodeSize : Integer;
  140.         {thanks to Bob Tolz and Randy Forgaard for this function}
  141.       VAR
  142.         i : Byte;
  143.       BEGIN
  144.         i := 11;
  145.         WHILE NOT((Mem[DSeg-2 : i+3] <> $00E9) AND (MemW[DSeg-2 : i+4] = $0000)) AND
  146.         NOT((MemW[DSeg-2 : i+0] = $00E9) AND (MemW[DSeg-2 : i+2] = $E800)) DO
  147.           i := i+1;
  148.         CodeSize := ((((DSeg-2)-CSeg) SHL 4)+i+6)-$100
  149.       END {CodeSize} ;
  150.  
  151.     BEGIN
  152.       WriteLn('You will now clone a copy of the boot record into this program...');
  153.       WriteLn('The completed version will be written to ',ProgramName,'.');
  154.       Write('Place a DOS formatted disk in drive A: and press any key when ready.');
  155.       repeat until keypressed;
  156.       WriteLn;
  157.       {read the boot record}
  158.       tries := 0;
  159.       REPEAT
  160.         tries := Succ(tries);
  161.         BIOSreadSectors(2, 0, 1, 0, 0, 1, BR, error);
  162.       UNTIL (error = 0) OR (tries = 3);
  163.  
  164.       IF error <> 0 THEN BEGIN
  165.         WriteLn('Could not read boot record.');
  166.         Halt;
  167.       END;
  168.       {clone this program}
  169.       Assign(f, ProgramName);
  170.       Rewrite(f, 1);
  171.       BlockWrite(f, Mem[CSeg : $100], CodeSize);
  172.       Close(f);
  173.       Halt;
  174.     END {initboot} ;
  175.  
  176.   FUNCTION DOSversion : Byte;
  177.       { return the major version number of DOS}
  178.     BEGIN
  179.       reg.ah := $30;
  180.       MsDos(reg);
  181.       DOSversion := reg.al;
  182.     END {DOSversion} ;
  183.  
  184.   FUNCTION ATmachine : Boolean;
  185.       { Return true if machine is AT class}
  186.     VAR
  187.       machtype : Byte ABSOLUTE $FFFF : $000E;
  188.     BEGIN
  189.       ATmachine := (machtype = $FC);
  190.     END {ATmachine} ;
  191.  
  192.   PROCEDURE readDASD(drive : Byte; VAR dType : Byte);
  193.       { read dasd for DOS 3 }
  194.       { whatever dasd is! }
  195.     BEGIN
  196.       reg.ah := $15;
  197.       reg.dl := drive;
  198.       Intr($13, reg);
  199.       IF Odd(reg.flags AND 1) THEN BEGIN
  200.         WriteLn('error reading DASD for format...');
  201.         Halt;
  202.       END;
  203.       dType := reg.ah;
  204.     END {readdasd} ;
  205.  
  206.   PROCEDURE setDASD(drive, dType : Byte);
  207.       { execute int 13 to "set DASD" for format of 360K disks on 1.2MB
  208.         floppies}
  209.     VAR
  210.       tries : Byte;
  211.     BEGIN
  212.       tries := 0;
  213.       REPEAT
  214.         tries := Succ(tries);
  215.         reg.ah := $17;
  216.         reg.al := dType;
  217.         reg.dl := drive;
  218.         Intr($13, reg);
  219.       UNTIL (tries = 3) OR NOT(Odd(reg.flags AND 1));
  220.  
  221.       IF Odd(reg.flags AND 1) THEN BEGIN
  222.         WriteLn('error setting DASD for format...');
  223.         Halt;
  224.       END;
  225.     END {setdasd} ;
  226.  
  227.   PROCEDURE InitFAT;
  228.       { initialize a FAT sector}
  229.     BEGIN
  230.       {fill fat with all zeros}
  231.       FillChar(FAT, 1024, 0);
  232.       {fill in the ID Bytes}
  233.       FAT[0] := $FD;          {9 sector DSDD drive}
  234.       FAT[1] := $FF;          {boilerplate}
  235.       FAT[2] := $FF;
  236.     END {initfat} ;
  237.  
  238.   PROCEDURE InitDiskBase;
  239.       { modify the disk base data per DOS 3 instructions}
  240.     BEGIN
  241.       {save old pointer}
  242.       OldDiskBase := BiosDiskBase;
  243.       {make a new disk base data area}
  244.       New(BiosDiskBase);
  245.       {put the data from the old area in the new one}
  246.       BiosDiskBase^ := OldDiskBase^;
  247.       {modify per dos 3 instructions, doesn't hurt on DOS 2}
  248.       BiosDiskBase^.glf := $50;
  249.       BiosDiskBase^.eot := 9;
  250.     END {initdiskbase} ;
  251.  
  252.   PROCEDURE Format(drive : Byte; VAR FMT : FormatArray; VAR error : Integer);
  253.       { lay down format tracks }
  254.     VAR
  255.       i : Integer;
  256.     BEGIN
  257.       {initialize format table}
  258.       FOR i := 1 TO 9 DO
  259.         WITH FMT[i] DO BEGIN
  260.           cyl := 0;           {cylinder number, will fill in during format}
  261.           hed := 0;           {head number}
  262.           rec := i;           {sector number}
  263.           num := 2;           {indicates 512 bytes per sector}
  264.         END;
  265.       FOR i := 1 TO 9 DO
  266.         WITH FMT[i+9] DO BEGIN
  267.           cyl := 0;           {cylinder number, will fill in during format}
  268.           hed := 1;           {head number}
  269.           rec := i;           {sector number}
  270.           num := 2;           {indicates 512 bytes per sector}
  271.         END;
  272.       {write the format information}
  273.       INLINE(
  274.         $8A/$56/$0C/          {MOV    DL,[BP+0C] - get drive number}
  275.         $C4/$5E/$08/          {LES    BX,[BP+08] - get pointer to format array}
  276.         $B9/$01/$00/          {MOV    CX,0001 - track 0 sector 1}
  277.  
  278.         {nexttrack: - loop over 40 disk tracks}
  279.         $8B/$FB/              {MOV    DI,BX - index into format array}
  280.         $B0/$12/              {MOV    AL,12 - number of sectors per track = 18}
  281.  
  282.         {inittrack: - loop over 18 sectors per track}
  283.         $26/$88/$2D/          {MOV    ES:[DI],CH - track track number in format array}
  284.         $81/$C7/$04/$00/      {ADD    DI,0004}
  285.         $FE/$C8/              {DEC    AL}
  286.         $75/$F5/              {JNZ    inittrack}
  287.  
  288.         $B6/$00/              {MOV    DH,00 - format 9 sectors on side 0}
  289.         $B8/$01/$05/          {MOV    AX,0501}
  290.         $CD/$13/              {INT    13}
  291.         $72/$18/              {JB     error - check for errors}
  292.  
  293.         $B6/$01/              {MOV    DH,01 - format 9 sectors on side 1}
  294.         $B8/$01/$05/          {MOV    AX,0501}
  295.         $53/                  {PUSH    BX}
  296.         $81/$C3/$24/$00/      {ADD    BX,0024}
  297.         $CD/$13/              {INT    13}
  298.         $72/$0A/              {JB     error - check for errors}
  299.  
  300.         $5B/                  {POP    BX}
  301.         $FE/$C5/              {INC    CH - next track}
  302.         $80/$FD/$28/          {CMP    CH,28}
  303.         $75/$D2/              {JNZ    nexttrack}
  304.  
  305.         $31/$C0/              {XOR    AX,AX - no errors, return 0}
  306.  
  307.         {error:}
  308.         $C4/$7E/$04/          {LES    DI,[BP+04]}
  309.         $26/$89/$05           {MOV    ES:[DI],AX - return error code}
  310.         );
  311.     END {format} ;
  312.  
  313.   PROCEDURE Verify(drive : Byte; VAR FAT : FATtable; VAR error : Integer);
  314.       { verify that sectors were formatted }
  315.     VAR
  316.       t, h : Integer;
  317.       cluster, fatofs, topcluster, content : Integer;
  318.     BEGIN
  319.       { initialize the verify buffer - 9 sectors * 512 bytes }
  320.       FillChar(VB, 4608, $F6);
  321.       { verify all sectors }
  322.       FOR t := 0 TO 39 DO
  323.         FOR h := 0 TO 1 DO BEGIN
  324.           BIOSreadSectors(4, drive, 1, t, h, 9, VB, error);
  325.  
  326.           IF error <> 0 THEN BEGIN
  327.             { mark the clusters on this track as unavailable }
  328.             cluster := ((9*(h+2*t)) DIV 2)-4;
  329.             topcluster := cluster+5;
  330.             WHILE cluster < topcluster DO BEGIN
  331.               fatofs := (3*cluster) DIV 2;
  332.               {get a word from the FAT}
  333.               Move(FAT[fatofs], content, 2);
  334.               {replace 12 bits of the word}
  335.               IF Odd(cluster) THEN
  336.                 content := content OR $FF70
  337.               ELSE
  338.                 content := content OR $0FF7;
  339.               {store it back}
  340.               Move(content, FAT[fatofs], 2);
  341.               cluster := Succ(cluster);
  342.             END;
  343.           END;
  344.         END;
  345.     END {verify} ;
  346.  
  347.   PROCEDURE InitDIR;
  348.       { initialize a sector for the root directory}
  349.     VAR
  350.       i : Integer;
  351.     BEGIN
  352.       {fill with format bytes}
  353.       FillChar(SB, 512, $F6);
  354.       {mark each directory entry as available}
  355.       FOR i := 1 TO 481 DO
  356.         IF ((i-1) MOD 32) = 0 THEN SB[i] := #0;
  357.     END {initdir} ;
  358.  
  359.  
  360. function FormatDisk(Parameter : Integer;
  361.                     DoVerify : boolean): Integer;
  362.  
  363. BEGIN
  364.  
  365.     CASE Parameter OF
  366.       -1 : InitBoot;     {clone the boot record into this program}
  367.     ELSE
  368.       drive := Parameter;
  369.     END;
  370.  
  371.     {make sure the boot record has been cloned into program}
  372.     IF BR[1] = #0 THEN BEGIN
  373.       WriteLn('You must first initialize this program.');
  374.       WriteLn('Type ',ProgramName,' @ <Enter> to initialize.');
  375.       Halt;
  376.     END;
  377.  
  378.     {get BIOS drive number}
  379.  
  380.     IF ATmachine AND (DOSversion = 3) THEN
  381.       BEGIN
  382.  
  383.         {get the drive type, necessary when dealing with 1.2MB drives}
  384.         readDASD(drive, dType);
  385.         IF (dType = 0) OR (dType = 3) THEN BEGIN
  386.           WriteLn('Drive is not present or non-removable.');
  387.           Halt;
  388.         END;
  389.         IF dType = 2 THEN
  390.           begin
  391.             WriteLn('Cannot format a 360K floppy in 1.2MB drive.');
  392.             Halt;
  393.           end;
  394.  
  395.         { Set the DASD type accordingly}
  396.         setDASD(drive, dType);
  397.  
  398.       END;
  399.  
  400.       { Set up the disk_base table }
  401.       InitDiskBase;
  402.  
  403.       { Lay down format tracks }
  404.       Format(drive, FMT, error);
  405.  
  406.       { Restore the disk_base table }
  407.       BiosDiskBase := OldDiskBase;
  408.  
  409.       IF error <> 0 THEN BEGIN
  410.         FormatDisk := Error;
  411.         Exit;
  412.       END;
  413.  
  414.       { Initialize the FATtable}
  415.       InitFAT;
  416.  
  417.       IF doVerify THEN BEGIN
  418.         {verify sectors}
  419.         Verify(drive, FAT, error);
  420.         IF error <> 0 THEN
  421.           begin
  422.             WriteLn('Bad disk, format not verified...');
  423.             FormatDisk := Error;
  424.           end
  425.       END;
  426.  
  427.       {write the boot record}
  428.       BIOSwriteSectors(drive, 1, 0, 0, 1, BR, error);
  429.  
  430.       {write the FAT sectors}
  431.       Move(FAT[0], SB, 512);
  432.       BIOSwriteSectors(drive, 2, 0, 0, 1, SB, error);
  433.       Move(FAT[512], SB, 512);
  434.       BIOSwriteSectors(drive, 3, 0, 0, 1, SB, error);
  435.       Move(FAT[0], SB, 512);
  436.       BIOSwriteSectors(drive, 4, 0, 0, 1, SB, error);
  437.       Move(FAT[512], SB, 512);
  438.       BIOSwriteSectors(drive, 5, 0, 0, 1, SB, error);
  439.  
  440.       {write the root directory}
  441.       InitDIR;
  442.       BIOSwriteSectors(drive, 6, 0, 0, 1, SB, error);
  443.       BIOSwriteSectors(drive, 7, 0, 0, 1, SB, error);
  444.       BIOSwriteSectors(drive, 8, 0, 0, 1, SB, error);
  445.       BIOSwriteSectors(drive, 9, 0, 0, 1, SB, error);
  446.       BIOSwriteSectors(drive, 1, 0, 1, 1, SB, error);
  447.       BIOSwriteSectors(drive, 2, 0, 1, 1, SB, error);
  448.       BIOSwriteSectors(drive, 3, 0, 1, 1, SB, error);
  449.  
  450.       FormatDisk := Error;
  451.  
  452. END;
  453.  
  454. procedure Beep;
  455.  
  456. begin
  457.   Sound(420);
  458.   Delay(20);
  459.   NoSound;
  460. end;
  461.  
  462. const
  463.   VerifyFormat = False;
  464.   drivea = 0;
  465.   driveb = 1;
  466.  
  467. type
  468.   { Holds sector data during sector setup after formatting}
  469.   TrackBuffer = array[1..9] of SectorBuffer;
  470.   TrackPtr = ^TrackBuffer;
  471.   diskarray = array[0..39, 0..1] of TrackPtr;
  472.  
  473. var
  474.   data : diskarray;
  475.   count : Integer;
  476.   t1 : Real;
  477.  
  478.   procedure BIOSdiskReset;
  479.     { reset the diskettes after an error}
  480.   begin
  481.     reg.ah := 0;
  482.     Intr($13, reg);
  483.   end {BIOSdiskReset} ;
  484.  
  485.   function Cardinal(i : Integer) : Real;
  486.     { return a real 0..65535}
  487.   var
  488.     r : Real;
  489.   begin                       {Cardinal}
  490.     r := i;
  491.     if r < 0.0 then r := r+65535.0;
  492.     Cardinal := r;
  493.   end {Cardinal} ;
  494.  
  495.   procedure ReadMasterDisk(drive : Byte);
  496.     { read a full DSDD floppy from drive A}
  497.   var
  498.     t, s, h : Integer;
  499.     error : Integer;
  500.     ch : Char;
  501.     tries : Integer;
  502.   begin                       {ReadMasterDisk}
  503.     Write(0:2);
  504.     for t := 0 to 39 do begin
  505.       Write(^H^H, t:2);
  506.       for h := 0 to 1 do begin
  507.         New(data[t, h]);
  508.         tries := 0;
  509.         repeat
  510.           tries := Succ(tries);
  511.           BIOSreadSectors(2, drive, 1, t, h, 9, data[t, h]^, error);
  512.           if error = 9 then   { 64K DMA boundary condition }
  513.             New(data[t, h])
  514.           else
  515.             if error <> 0 then
  516.               BIOSdiskReset;
  517.         until (error = 0) or (tries > 3);
  518.         if error <> 0 then begin
  519.           Write(^H^H'  ');
  520.           WriteLn;
  521.           WriteLn('Cannot read master disk'^G);
  522.           Write('Insert diskette and press a key... ');
  523.           Read(Kbd, ch);
  524.           WriteLn;
  525.         end;
  526.       end;
  527.     end;
  528.     Write(^H^H'  ');
  529.   end {ReadMasterDisk} ;
  530.  
  531.   procedure WriteDisk(drive : Byte);
  532.     { write a full DSDD floppy from drive A}
  533.   label
  534.     retry;
  535.   var
  536.     t, s, h : Integer;
  537.     error : Integer;
  538.     good : Boolean;
  539.     ch : Char;
  540.  
  541.     function time : Real;
  542.       { return time of day in seconds since midnight}
  543.     begin
  544.       reg.ah := $2C;
  545.       MsDos(reg);
  546.       time := 1.0*(reg.dh+60.0*(reg.cl+60.0*reg.ch)+reg.dl/100.0);
  547.     end {time} ;
  548.  
  549.     procedure BreakPressed;
  550.       { halt if Ctrl-C is pressed}
  551.     var
  552.       ch : Char;
  553.     begin                     {breakpressed}
  554.       while KeyPressed do begin
  555.         Read(Kbd, ch);
  556.         if ch = ^C then begin
  557.           WriteLn('^C');
  558.           Halt(2);
  559.         end;
  560.       end;
  561.     end {breakpressed} ;
  562.  
  563.   begin
  564.  
  565.     t1 := time;
  566.     repeat
  567.  
  568.       breakpressed;
  569.       WriteLn;
  570.       Write(count:3, ' Writing to drive ', Chr(drive+65), ':, track number ');
  571.       write(0:2);
  572.  
  573.       for t := 0 to 39 do begin
  574.         write(^H^H, t:2);
  575.         for h := 0 to 1 do begin
  576.           BIOSwriteSectors(drive, 1, t, h, 9, data[t, h]^, Error);
  577.           if Error <> 0 then
  578.             begin
  579.               case Error of
  580.                 $80 : begin { Timeout }
  581.                         Beep;
  582.                         writeln;
  583.                         write('    Drive not ready.  Insert disk and press any key... ');
  584.                      end;
  585.                 02 : begin
  586.                        writeln;
  587.                        write('    Disk is unformatted.  Formatting disk...');
  588.                        Error := FormatDisk(Drive, VerifyFormat);
  589.                        if Error <> 0 then
  590.                          begin
  591.                            Beep;
  592.                            write('Error formatting disk.');
  593.                          end
  594.                        else
  595.                          writeln('Format complete.');
  596.                      end;
  597.                 03 : begin
  598.                        writeln;
  599.                        Beep;
  600.                        write('    Disk is write protected.  Press any key to continue.');
  601.                      end
  602.                 else
  603.                   begin
  604.                     writeln;
  605.                     Beep;
  606.                     write('    Disk may be unusable.  Press any key to continue.');
  607.                   end
  608.               end;
  609.               if Error <> 0 then
  610.                 begin
  611.                   repeat until KeyPressed;
  612.                   BreakPressed;
  613.                   writeln;
  614.                 end;
  615.               Good := false;
  616.               BIOSdiskReset;
  617.               Goto Retry;
  618.             end;
  619.           end;
  620.         end;
  621.  
  622.       count := Succ(count);
  623.       good := True;
  624.  
  625.  
  626. retry:
  627.  
  628.     until Good;
  629.     writeln('    Elapsed time: ',(time-t1):0:2, ' seconds.');
  630.  
  631.   end {WriteDisk} ;
  632.  
  633.  
  634. begin
  635.  
  636.   if ParamStr(1) = '@' then
  637.     Error := FormatDisk(-1, VerifyFormat);
  638.  
  639.   WriteLn;
  640.  
  641.   {check for sufficient memory}
  642.   if 16.0*Cardinal(MemAvail) < 370000.0 then begin
  643.     WriteLn('Insufficient RAM space to read master disk');
  644.     Halt;
  645.   end;
  646.  
  647.   {prompt to read from drive a:}
  648.   Write('Insert master disk into drive A. Press a key when ready... ');
  649.   Read(Kbd, ch);
  650.   WriteLn;
  651.  
  652.   {read the disk into memory}
  653.   ReadMasterDisk(drivea);
  654.  
  655.   WriteLn;
  656.   WriteLn('Insert first disk to copy in drive A.');
  657.   WriteLn('Copies will alternate from A to B. Press ^C to stop.');
  658.   Write('Press a key when ready... ');
  659.   Read(Kbd, ch);
  660.   WriteLn;
  661.   WriteLn;
  662.   count := 1;
  663.  
  664.   { loop copying to drives a and b }
  665.   repeat
  666.     WriteDisk(drivea);
  667.     WriteDisk(driveb);
  668.   until False;
  669.  
  670. end.
  671.