PROGRAM cpmcopy;

(*Written by J. M. Wierda

    This program will transfer a CP/M file from a disk in unit 5 to a Pascal
disk in unit 4. Note that when the filename is requested, it must be 11
characters long and include all spaces. This program does not remove the LFs
from the CP/M file as the transfer is made, so a replace command in the Pascal
editor should be used to delete the LFs. During successful file transfers the
program prints an expanded CP/M directory of the file being tranferred.
Attempts to transfer empty or non-existent files are ignored.*)

CONST
     blkslip = 2;
     quadsperblk = 4;
     trkslip = 6;
     blkspertrk = 6.5;
     secpergrp = 8;
     lastsec = 26;
     lasttrk = 76;

TYPE
   groupbuffer = packed array[0..1023] of char;

VAR
   sectortbl : ARRAY[1..lastsec] OF integer;
   grptrk : ARRAY[1..secpergrp] OF integer;
   grpsec : ARRAY[1..secpergrp] OF integer;
   quadmap : ARRAY[1..lastsec] OF integer;
   blockmap : ARRAY[1..lastsec] OF integer;
   blockbuffer : PACKED ARRAY[0..511] OF char;
   directbuffer : groupbuffer;
   transferbuffer : groupbuffer;
   prevtrk, filectr : integer;
   currfile, filename : string;
   outfile : string;
   file2 : text;

PROCEDURE trackmap(track : integer);

VAR
   sector,sect,blk,quad,firstsect : integer;
   firstblkquad : real;

BEGIN
 IF track <> prevtrk
 THEN
   BEGIN
     firstblkquad := (track-1)*blkspertrk;
     blk := trunc(firstblkquad);
     firstsect := (((track-1)*trkslip)+1) MOD lastsec;
     quad := trunc((firstblkquad-blk)*quadsperblk);
     sect := firstsect;
     FOR sector := 1 TO lastsec DO
       BEGIN
         blockmap[sect] := blk;
         quadmap[sect] := quad;
         sect := (sect + blkslip) MOD lastsec;
         IF sect = 0
         THEN
           sect := lastsec;
         IF sect = firstsect
         THEN
           sect := sect + 1;
         quad := (quad+1) MOD quadsperblk;
         IF quad = 0
         THEN
           blk := blk + 1
       END
   END
END;

PROCEDURE initsectbl;
BEGIN
 sectortbl[1] := 1;
 sectortbl[2] := 7;
 sectortbl[3] := 13;
 sectortbl[4] := 19;
 sectortbl[5] := 25;
 sectortbl[6] := 5;
 sectortbl[7] := 11;
 sectortbl[8] := 17;
 sectortbl[9] := 23;
 sectortbl[10] := 3;
 sectortbl[11] := 9;
 sectortbl[12] := 15;
 sectortbl[13] := 21;
 sectortbl[14] := 2;
 sectortbl[15] := 8;
 sectortbl[16] := 14;
 sectortbl[17] := 20;
 sectortbl[18] := lastsec;
 sectortbl[19] := 6;
 sectortbl[20] := 12;
 sectortbl[21] := 18;
 sectortbl[22] := 24;
 sectortbl[23] := 4;
 sectortbl[24] := 10;
 sectortbl[25] := 16;
 sectortbl[lastsec] := 22
END;

PROCEDURE cpmgrp(group : integer);

VAR
   j, track, sector : integer;

BEGIN
 track := ((group * 8) DIV lastsec) + 2;
 sector := ((group * 8) MOD lastsec) + 1;
 FOR j := 1 TO secpergrp DO
   BEGIN
     grptrk[j] := track;
     grpsec[j] := sectortbl[sector];
     sector := sector + 1;
     IF sector > lastsec
     THEN
       BEGIN
         sector := 1;
         track := track + 1
       END
   END
END;

procedure readgroup(group : integer; VAR buffer : groupbuffer);
var
  j, k, l : integer;
begin
 cpmgrp(group);
 l := 0;
 for j := 1 to secpergrp do
   begin
     trackmap(grptrk[j]);
     unitread(10,blockbuffer,512,blockmap[grpsec[j]],0);
     for k := ((quadmap[grpsec[j]])*128) to k+127 do
       begin
         buffer[l] := blockbuffer[k];
         l := l+1
       end
   end
end;

PROCEDURE printentries;

VAR
   j, k : integer;

begin
 j := 0;
 while j < 1024 do
   BEGIN
     IF (ord(directbuffer[j]) = 0) AND (ord(directbuffer[j+12]) = 0)
        AND (directbuffer[j+1] IN [' '..'Z'])
     THEN
       BEGIN
         FOR k := j+1 TO j+11 DO
           write(directbuffer[k]);
         filectr := filectr + 1;
         IF (filectr MOD 4) = 0
         THEN
           writeln
         ELSE
           write('        ')
       END;
     j := j+32
   END
end;

PROCEDURE findentry;

VAR
   extent, sectors, j, k, l : integer;
   eoffound : boolean;
BEGIN
 j := 0;
 WHILE j < 1024  DO
   BEGIN
     IF ord(directbuffer[j]) = 0
     THEN
       BEGIN
         currfile := '           ';
         extent := ord(directbuffer[j+12]);
         sectors := ord(directbuffer[j+15]);
         FOR k := j+1 TO j+11 DO
           currfile[k-j] := directbuffer[k];
         IF (currfile = filename) AND (sectors > 0)
         THEN
           BEGIN
             IF extent = 0
             THEN
               BEGIN
                 write('Output Filename.Ext ? ');
                 readln(outfile);
                 rewrite(file2, outfile);
                 writeln(currfile);
                 writeln('Ex Sec Groups')
               END;
             write(extent: 2,sectors: 4);
             FOR k := j+16 TO (k+((sectors-1) DIV 8)) DO
               begin
                 write(ord(directbuffer[k]): 4);
                 readgroup(ord(directbuffer[k]), transferbuffer);
                 l := 0;
                 eoffound := false;
                 WHILE l <= 1023 DO
                   begin
                     IF (ord(transferbuffer[l]) <> 26) and (not eoffound)
                     then
                       write(file2, transferbuffer[l])
                     else
                       begin
                         eoffound := true;
                         write(file2, chr(0))
                       end;
                     l := l + 1
                   end;
               end;
             IF sectors < 128
             THEN
               close(file2,lock);
             writeln
           END
       END;
     j := j+32
   END
END;

BEGIN
 filectr := 0;
 prevtrk := 0;
 initsectbl;
 writeln('CP/M File Transfer, 7-Jun-79');
 readgroup(0, directbuffer);
 printentries;
 readgroup(1, directbuffer);
 printentries;
 writeln;
 writeln(filectr,' Files');
 REPEAT
   REPEAT
     writeln;
     write('Transfer which file ? ');
     readln(filename);
     IF NOT (length(filename) IN [0,11])
     THEN
       BEGIN
         write('Enter 11 character filename exactly as listed');
         writeln(', including spaces,');
         writeln('or CR to exit program.')
       END
   UNTIL length(filename) IN [0,11];
   IF length(filename) = 11
   THEN
     BEGIN
       readgroup(0, directbuffer);
       findentry;
       readgroup(1, directbuffer);
       findentry
     END;
 UNTIL length(filename) = 0;
END