Program ATARIO(Input,Output);

{
Feel free to copy, distribute and modify this program to your hearts
content.  I retain no rights to it, nor do I care to hear complaints
about it.  Send any praises that you desire.

Dave Brandman
805 Rotherham Drive
Manchester, MO  63011
}

{
I took heed of the previos statement and modified the program.  It will
now work on an XF551 disk, formatted DS/DD under MyDOS 4.5.  There is an
option in the configure menu to use "other drives".  This sets the program
back the way it was (which may have been correct for older drives, like the
Percom).  There should be no need to use this option with a SS/DD disk.  There
is also an option to use another physical drive on the PC which uses absolute
drive numbers.  After changing drives, get a directory to see if it is the
right one.  Lastly, I added the option to change the colors.  This program
works with Turbo Pascal 3.0 and with Turbo Pascal 5.5.

Kevin White
235 Race St.
Berea, OH 44017
September 6, 1991
}

Uses Turbo3, Crt, Dos;      {Comment this line out if you use Turbo 3.0
                             If you use Turbo 3.0, a change needs to be
                             made in the Getsec function.  Look there before
                             compiling!}

Type
   Str          = Array [1..256] of Byte;
   Bstring      = Array [1..1000] of Char;
   Lstring      = String[128];

Var
   File_Map,
   Buffer             : Str;
   Disk_Side,
   File_Sector_Index,
   Next_File_Sector,
   File_Sector_Size,
   Choice,
   Directory_Index,
   Directory_Sector,
   Orig_Sector_Value,
   Disk_Base_Ofs,
   Disk_Base_Seg,
   Sector_Size,
   Sector_Per_Track,
   Sector_Per_Cyl,
   Sides_Per_Disk,
   Dos_Sector,
   Bios_Sector_Size,
   Bios_Sector,
   Bios_Track,
   Bios_Side,
   Drive,
   File_Map_Index,
   Start_Directory,
   Last_LJK_Sector_Size,
   Cmd,
   XF551,
   ColorText, ColorBack,
   Diskio_Status      : Integer;
   Diskio_Error,
   Pending_Eof,
   Eof                : Boolean;
   Max_Entries,
   Dos_Mode,
   File_Entry_Status  : Byte;

Procedure Init;

Begin   { Init }
   Sector_Size := 256;     { ATARI Format }
   Bios_Sector_Size := 1;  { 256 Byte Sectors }
   Sector_Per_Track := 18; { ATARI Format }
   Sides_Per_Disk := 1;    { ATARI Format }
   Drive := 0;             { Drive A      }
   Sector_Per_Cyl := Sector_Per_Track * Sides_Per_Disk;
   Disk_Base_Ofs := Memw[0:120];
   Disk_Base_Seg := Memw[0:122];
   Orig_Sector_Value := Mem[Disk_Base_Seg:Disk_Base_Ofs+3];
   Disk_Side := 1;
   Dos_Mode := 0;
   Max_Entries := 8;
   Start_Directory := 361;
   XF551 := 1;
   Cmd := 2;               { Read }
   HighVideo;
   ColorText := 10;
   ColorBack := 4;
   TextColor (ColorText);
   TextBackground (ColorBack);
End;

Function Compare(str1,str2 : Lstring) : Boolean;

Var
   len1,
   len2,
   i     : Integer;

Begin
   Compare := False;
   len1 := length(str1);
   len2 := length(str2);
   If (len1 = len2) Then Begin
      I := 1;
      While (upcase(str1[i]) = upcase(str2[i])) and (i <= len1) do
         i := succ(i);
      If i > len1 Then
         Compare := True;
   End;
End;

Function Hex1(num : integer) : lstring;

Var
   hib, lob : Integer;

Begin    { Hex1 }
   hib := lo(num shr 4 or $30);
   If hib > 57 Then
      hib := hib + 7;
   lob := lo(num and $0F or $30);
   If lob > 57 Then
      lob := lob + 7;
   hex1 := char(hib) + char(lob);
End;

Procedure Pause;

Var
   Ch : Char;

Begin  { Pause }
   Gotoxy(1,25);
   Write('Hit Any Key to Continue');
   Repeat
   Until
      Keypressed;
   Read(Kbd,Ch)
End;

Function Get_Choice(Low, High : Integer) : Integer;

Var
   Ch     : Char;
   Choice : Integer;

Begin
   Repeat
      Gotoxy(1,24); Write('===> ___    ');
      Gotoxy(6,24);
      Repeat
      Until
         Keypressed;
      Read(Kbd,Ch);
      If (Ch = #27) and (Keypressed) Then Begin
         Read(Kbd,Ch);
         Choice := Ord(Ch) - 58;
         If Choice = 10 Then
            Choice := 0;
         End
      Else
         Choice := Ord(Ch) - 48;
   Until
      (Choice >= Low) and (Choice <= High);
   Get_Choice := Choice;
   Clrscr;
End;

Function Getyn : Char;

Var
   Ch     : Char;

Begin
   Repeat
      Repeat
      Until
         Keypressed;
      Read(Kbd,Ch);
      Ch := Upcase (Ch)
   Until
      (Ch = 'Y') or (Ch = 'N');
   Getyn := Ch;
End;

Function Getsec : Integer;

var
   i,
   Attempts,
   Diskio_Status : Integer;
   Regs : Registers;  {Comment out this line and un-comment the next group
                       if you have Turbo Pascal 3.0}
{  Regs : Record
             Ax    : Integer;
             Bx    : Integer;
             Cx    : Integer;
             Dx    : Integer;
             Bp,Si,Di,Ds : Integer;
             Es    : Integer;
             Flags : Integer;
          End;      }


Begin   { Getsec }
   Attempts := 0;
   Diskio_Status := -1;
   While (Diskio_Status <> 0) and (Attempts < 3) Do Begin
      Mem[Disk_Base_Seg:Disk_Base_Ofs+3] := Bios_Sector_Size;
      Regs.Es := Seg(Buffer);
      Regs.Bx := Ofs(Buffer);
      Regs.Dx := Drive + Swap(Bios_Side);
      Regs.Cx := Bios_Sector + Swap(Bios_Track);
      Regs.Ax := Swap(Cmd) + 1;
      Intr($13,regs);
      Diskio_Status := Hi(Regs.Ax);
      Mem[Disk_Base_Seg:Disk_Base_Ofs+3] := Orig_Sector_Value;
      Attempts := Succ(Attempts);
      If Diskio_Status <> 0 Then Begin
         Regs.Ax := $0000;
         Intr($13,regs); End
      Else
         For I := 1 to Sector_Size Do
            Buffer[I] := Not Buffer[I];
   End;
   Getsec := Diskio_Status;
End;

Procedure Display_Status(secnum : integer);

Begin      { Display_Status }
   Writeln;
   Writeln('Dos_Sector  Bios_Side   Bios_Track   Bios_Sector   Return_Code');
   Gotoxy(3,9); Write(secnum:3);
   Gotoxy(16,9); Write(Bios_Side:1);
   Gotoxy(28,9); Write(Bios_Track:2);
   Gotoxy(42,9); Write(Bios_Sector:2);
   Gotoxy(56,9); Writeln(Diskio_Status:2);
   Writeln; Writeln;
End;

Procedure Get_Sector(Secnum : Integer);

Begin   { Get_Sector }
   If Disk_Side = 2 Then
      Secnum := 1440 - Secnum;
   If Secnum <= 720 Then
      Bios_Side := 0
   Else Begin
      Bios_Side := 1;
      Secnum := (1440 + XF551) - Secnum;
   End;
   Diskio_Error := False;
   Bios_Sector := Secnum mod Sector_Per_Track;
   If Bios_Sector = 0 Then
      Bios_Sector := Sector_Per_Track;
   Bios_Track := Secnum div Sector_Per_Track;
   If Bios_Sector = Sector_Per_Track Then
      Bios_Track := Bios_Track - 1;
   If (Bios_Track >= 0) and (Bios_Track < 41) Then
      Diskio_Status := Getsec
   Else
      Diskio_Status := 64;
   If Diskio_Status <> 0 Then
      Diskio_Error := True;
End;


Procedure Display_Sector;

Var
   Byt   : Byte;
   I,
   J,
   Index : Integer;
   Chr1  : Char;
   Hexstring,
   Asciistring : lstring;

Begin   { Display_Sector }
   Write('      00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F');
   Writeln('      ----+----+----+-');
   Index := 1;
   For I := 1 to Sector_Size div 16 Do Begin
      Hexstring := '';
      Asciistring := '';
      For J := 1 to 16 Do Begin
         Byt := Buffer[Index];
         Index := succ(Index);
         hexstring := hexstring + hex1(Ord(Byt)) + ' ';
         Case Byt Of
            $00..$19 : chr1 := '.';
            $20..$7E : chr1 := Char(Byt);
            $7F..$9F : chr1 := '.';
            $A0..$FE : chr1 := Char(Byt-128);
            $FF      : chr1 := '.';
         End;
         asciistring := asciistring + chr1;
      End;
      Writeln('  ',hex1((i-1)*16),': ',hexstring,'     ',asciistring);
   End;
   Writeln;
End;

Procedure Get_Next_File_Sector;

Begin   { Get_Next_File_Sector }
   If Next_File_Sector > 0 Then Begin
      Get_Sector(Next_File_Sector);
      If Dos_Mode = 0 Then Begin
         File_Sector_Size := Buffer[Sector_Size];
         Next_File_Sector := Buffer[Sector_Size-1] +
            swap(Buffer[Sector_Size-2]);
         If File_Entry_Status = 0 Then
            Next_File_Sector := Next_File_Sector and $03FF;
         End
      Else Begin
         File_Sector_Size := Sector_Size;
         Next_File_Sector := File_Map[File_Map_Index] +
            Swap(File_Map[File_Map_Index + 1]);
         If Next_File_Sector = 0 Then
            File_Sector_Size := Last_LJK_Sector_Size;
         File_Map_Index := File_Map_Index + 2;
      End;
      File_Sector_Index := 1;
      End
   Else
      Pending_Eof := True;
End;

Function Get_Next(var entry : lstring) : Boolean;

Var
   i      : integer;

Begin      { Get_Next }
   Repeat
      If directory_index = Max_Entries Then Begin
         directory_sector := succ(directory_sector);
         directory_index := 0;
         Get_Sector(directory_sector);
      End;
      File_Entry_Status := buffer[directory_index * 16 + 1];
      If Directory_Sector > Start_Directory + 8 Then
         File_Entry_Status := 0;
      If File_Entry_Status = 0 Then
         Get_Next := False
      Else If File_Entry_Status < 128 Then Begin
         Get_Next := True;
         entry[0] := chr(16);
         For I := 1 to 16 Do
            entry[i] := Chr(buffer[Directory_Index * 16 + I]);
      End;
      Directory_Index := succ(Directory_Index);
   Until
      File_Entry_Status < 128;
End;

Function Open(Fn : Lstring ; Var File_Entry : Lstring) : Boolean;

Var
   Ext       : Lstring;
   More      : Boolean;
   Dot       : Integer;

Begin    { Open }
   Dot := Pos('.',Fn);
   If Dot = 0 Then
      insert('           ',Fn,length(Fn)+1)
   Else Begin
      ext := copy(Fn,dot+1,3) + '   ';
      delete(Fn,dot,11);
      Fn := Fn + '        ';
      Insert(ext,Fn,9);
   End;
   Fn[0] := Chr(11);
   Directory_Sector := Start_Directory-1;
   Directory_Index := Max_Entries;
   Repeat
      More := Get_Next(File_Entry);
   Until
      (Compare(Copy(File_Entry,6,11),Fn)) or (Not More);
   If More Then Begin
      Open := True;
      Next_File_Sector := Ord(File_Entry[4]) + Swap(Ord(File_Entry[5]));
      File_Entry_Status := File_Entry_Status and $04;
      If Dos_Mode = 0 Then
         Get_Next_File_Sector
      Else Begin
         Get_Sector(Next_File_Sector);
         Move(Buffer[9],File_Map[1],Sector_Size-8);
         Next_File_Sector := File_Map[1] + Swap(File_Map[2]);
         File_Map_Index := 3;
         Last_LJK_Sector_Size := Buffer[6];
         Get_Next_File_Sector;
      End;
      Eof := False;
      Pending_Eof := False;
      End
   Else
      Open := False;
End;

Procedure Readrcd(Var Rcd : Bstring ; Var Rcd_Index : Integer);

Var
   Byt  : Char;

Begin   { Readrcd }
   Rcd_Index := 0;
   If Not Pending_Eof Then
      Repeat
         Byt := Chr(Buffer[File_Sector_Index]);
         File_Sector_Index := Succ(File_Sector_Index);
         If File_Sector_Index > File_Sector_Size Then
            Get_Next_File_Sector;
         If Ord(Byt) <> $9B Then Begin
            Rcd_Index := Succ(Rcd_Index);
            Rcd[Rcd_Index] := Byt;
         End;
      Until
         (Ord(Byt) = $9B) or (Pending_Eof)
   Else
      Eof := True;
End;

Procedure Copy_File;

Var
   Rcd    : Bstring;
   I      : Integer;
   Rcd_Len : Integer;
   Entry,
   Fn     : Lstring;
   Choice : Integer;
   Out_Fn : Text;
   DiskError : Boolean;

Begin     { Copy_File }
   Gotoxy(10,8);  Write('Copy File   ATARI -> IBM');
   Gotoxy(10,12); Write('1: Text Copy');
   Gotoxy(10,14); Write('2: Image Copy');
   Gotoxy(10,19); Write('0: Main Menu');
   Choice := Get_Choice(0,2);
   If Choice <> 0 then
     begin
       Write('Enter File Name: ');
       Readln(Fn);
       If Open(Fn,Entry) Then Begin
          Writeln('Copying : ',Copy(Entry,6,11)); Writeln;
          Assign(Out_Fn,Fn);
          Rewrite(Out_Fn);
          DiskError := false;
          Case Choice Of
             1: Begin
                   Readrcd(Rcd,Rcd_Len);
                   While Not Eof Do Begin
                      If Not Diskio_Error Then Begin
                         For I := 1 to Rcd_Len Do Begin
                            Write(Out_Fn,Rcd[I]);
                         End;
                         Writeln(Out_Fn);
                         Readrcd(Rcd,Rcd_Len); End
                      Else Begin
                         DiskError := True;
                         Eof := True;
                         Write ('There was an error reading the Atari');
                         Writeln (' disk.');
                         Writeln;
                         Write ('This is information about where the ');
                         Writeln ('error occurred.');
                         Display_Status(Next_File_Sector);
                         Close (Out_Fn);
                         Write ('Do you want to erase the file fragment ');
                         Write ('on the IBM disk (Y or N)?');
                         If (Getyn = 'Y')
                           then Begin
                             Erase (Out_Fn);
                             Writeln;
                             Writeln;
                             Writeln ('The file fragment has been erased.')
                           End
                      End;
                   End;
                End;
             2: While Not Pending_Eof Do Begin
                   If Not Diskio_Error Then Begin
                      For I := 1 to File_Sector_Size Do Begin
                         Write(Out_Fn,Chr(Buffer[I]));
                      End;
                      Get_Next_File_Sector; End
                    Else Begin
                      Pending_Eof := True;
                      DiskError := true;
                      Writeln ('There was an error reading the Atari disk.  ');
                      Writeln;
                      Write ('This is information about where the error ');
                      Writeln ('occurred.');
                      Display_Status(Next_File_Sector);
                      Close (Out_Fn);
                      Write ('Do you want to erase the file fragment ');
                      Write ('on the IBM disk (Y or N)?');
                      If (Getyn = 'Y')
                        then Begin
                          Erase (Out_Fn);
                          Writeln;
                          Writeln;
                          Writeln ('The file fragment has been erased.')
                        End
                   End;
                End;
          End;
          If not DiskError then Close(Out_Fn);
          End
       Else
          Writeln(Fn,' Not Found');
       Pause;
   End
End;

Procedure Directory_List;

var
   more  : boolean;
   entry : lstring;
   free,
   times,
   size  : integer;

Begin      { Directory_List }
   Times := 1;
   Directory_Sector := Start_Directory-1;
   Directory_Index := Max_Entries;
   More := Get_Next(Entry);
   While More Do Begin
      Size := Ord(Entry[2]) + Swap(Ord(Entry[3]));
      If Times < 23 Then
         Gotoxy(1,times)
      Else If Times < 45 Then
         Gotoxy(26,Times-22)
      Else
         Gotoxy(51,Times-44);
      Write(Copy(Entry,6,11),'   ',Size:3);
      More  := Get_Next(Entry);
      Times := Succ(Times);
      If (Times > 64) Then
         More := False;
   End;
   If Dos_Mode = 0 Then Begin
      Get_Sector(Start_Directory-1);
      Free := Buffer[4] + Swap(Buffer[5]);
      Gotoxy(1,23); Write(Free:4,' Free Sectors');
   End;
   Pause;
End;

Procedure Dump_Sectors;

Var
   Continue     : Boolean;
   last_sector  : Integer;

Begin
   Continue := True;
   Last_Sector := Start_Directory - 1;
   While Continue Do Begin
      Write('Which Sector: ');
      Dos_Sector := -1;
      {$I-} Readln(Dos_Sector); {$I+}
      If (Ioresult = 0) Then Begin
         If Dos_Sector = -1 Then
            Dos_Sector := Last_Sector + 1;
         Last_Sector := Dos_Sector;
         If Dos_Sector > 0  Then Begin
            Get_Sector(Dos_Sector);
            Display_Status(Dos_Sector);
            If Diskio_Status = 0 Then
               Display_Sector;
         End
         Else
            Continue := False;
      End
      Else
         Clrscr;
   End;
End;

Procedure Configure;

Var
   Choice : Integer;

Begin   { Configure }
   Repeat
      ClrScr;
      Gotoxy(10,3); Write('Configure Disk Parameters');
      Gotoxy(10,7); Write('1: Side ',Disk_Side:1);
      Gotoxy(10,9);
      Case Dos_Mode Of
         0: Write('2: DOS 2.x');
         1: Write('2: LJK DOS');
      End;
      Gotoxy (10,11);
      Case XF551 Of
         0: Write('3: Other Drive');
         1: Write('3: Atari XF551')
      End;
      Gotoxy(10,13);
      Write ('4: Drive ', Char(65+Drive));
      Gotoxy(10,15);
      Write ('5: Text Color Change');
      Gotoxy(10,17);
      Write ('6: Text Background Change');
      Gotoxy(10,22); Write('0: Main Menu');
      Choice := Get_Choice(0,6);
      Case Choice Of
         1: Disk_Side := 3 - Disk_Side;
         2: Begin
               Dos_Mode := 1-Dos_Mode;
               If Dos_Mode = 0 Then Begin
                  Start_Directory := 361;
                  Max_Entries := 8; End
               Else Begin
                  Start_Directory := 257;
                  Max_Entries := 16;
               End;
            End;
         3: XF551 := 1 - XF551;
         4: Begin
                Drive := Drive + 1;
                If Drive > 25 then Drive := 0
            End;
         5: Begin
              ColorText := ColorText + 1;
              If ColorText > 15 then ColorText := 0;
              TextColor (ColorText);
            End;
         6: Begin
              ColorBack := ColorBack + 1;
              If ColorBack > 7 then ColorBack := 0;
              TextBackground (ColorBack)
            End
      End;
   Until
      Choice = 0;
End;

Begin   { Main }
   Init;
   Repeat
      Choice := -1;
      Clrscr;
      Gotoxy(10,5)  ; Write('ATARI -> IBM File Copier');
      Gotoxy(10,6)  ; Write('Version 2.1');
      Gotoxy(10,10) ; Write('1: Dump Sectors');
      Gotoxy(10,12) ; Write('2: Copy File');
      Gotoxy(10,14) ; Write('3: Directory List');
      Gotoxy(10,16) ; Write('4: Configure');
      Gotoxy(10,19) ; Write('0: Exit');
      Choice := Get_Choice(0,4);
      Case Choice Of
         1:  Dump_Sectors;
         2:  Copy_File;
         3:  Directory_list;
         4:  Configure;
      End;
   Until Choice = 0;
   NormVideo;
   ClrScr
End.
