DOS/PASCAL Bilingual Disks

A Call-A.P.P.L.E. Technique
by Mat Davis
Call-A.P.P.L.E. Magazine October 1982 PP17

One of the Apple’s strongest points is its ability to use multiple operating systems such as DOS 3.3 and Pascal. For someone who uses both of these operating systems, there are times when it would be convenient to have disks that both Pascal and DOS could use. Such a disk would be useful for individual users who are trading programs or for user groups who want to have some Pascal programs in their library without using an entire disk for only 80 blocks of programs.
The Pascal program BILINGUAL will create a disk that may be used by both Pascal and DOS and make sure that neither operating system will overwrite the other’s files. Since the two systems handle files and free space in different ways, two methods are used to prevent conflicts.

Pascal allocates all files in contiguous blocks…

Pascal allocates all files in contiguous blocks on the disk and considers anything that is not in a file to be free space. Therefore, to keep the Pascal system from using a particular area of the disk, a file must be created in that area. Also, the file type should be set to BAD to prevent the Filer’s Krunch command from moving the file. For information on the Pascal directory format, the May ’81 issue of Byte contains an article entitled “A File Catalog System for UCSD Pascal” by Edward Heyman which has a program containing all of the declarations and code necessary to manipulate the directory. Another approach may be found in the article “In the Depths of the Pascal Directory” by Harvey Greenberg in the May ’81 issue of Call -A.P.P.L.E.; the article “Pascal Disk Directory Structure” by Allen W. Todd in Call -A.P.P.L.E. in Depth No.2: All About Pascal may also be of interest.
Modifying the directory is accomplished by using the Pascal intrinsic procedures UNITREAD and UNITWRITE. These are among the most powerful (and most dangerous) features of Apple Pascal. They allow the programmer to read from and write to any block on a disk, including the directory and system areas. With such procedures, it is possible to write programs entirely in Pascal that otherwise would be impossible to write without using assembly language. Care must be used with these procedures, however, to avoid destroying a disk inadvertently. It is best to always test programs using UNITREAD and UNITWRITE on a scratch disk first to make sure they perform as expected. Detailed information on these procedures can be found on page 41 of the Apple Pascal 1.1 Language Reference Manual.
To change the type of a file to BAD, the directory must be read from the second block of the disk, the file type changed, and the directory written back to the disk. To shorten this program, the directory is read into an array and the proper location changed without using the declarations from the Byte article, since very little manipulation of the directory is needed.

Unlike Pascal, DOS spreads files wherever there is space.

Unlike Pascal, DOS spreads files across the disk wherever there is space. Since the free areas of the disk are randomly distributed, DOS keeps a free space map in its Volume Table of Contents, or VTOC, which is stored in sector 0 of track 17 (decimal) on every DOS diskette. Therefore, to keep DOS from using a particular area of the disk, it is necessary to mark those areas as used in the free space map. An excellent book covering the disk format is “Beneath Apple DOS,” by Don Worth and Pieter Lecher, available from A.P.P.L.E. (for members only) or from Quality Software. The same information with considerably less explanation can be found in “Format of Disk Information” in Appendix C of the DOS manual.
To set up the DOS area of the disk, a single array is used in which the VTOC and directory are created. To create them, the procedure CREATEDOSDIR zeros out the array, sets up the VTOC, and links the directory sectors together. After it is created, the directory is written to track 17 of the disk using UNITWRITE. Since there are eight Pascal blocks per track, the block number for the beginning of track 17 is 8 * 17, or 136. Also, there are 256 bytes/sector * 16 sectors/track, so 16384 bytes must be written.

The conversion must be started with a Pascal disk…

The conversion must be started with a Pascal disk if the disk is to be bootable after conversion, since the Pascal directory occupies the same locations as the DOS boot tracks. To make the disk fully bootable, the files SYSTEM.APPLE and SYSTEM. PASCAL should be placed on the Pascal portion of the disk. Since only a Pascal disk will boot properly after the conversion, the program assumes that the disk to be converted is a properly formatted Pascal disk.
The main program does nothing more than provide a means for more than one disk to be converted without having to re-execute the program for each disk. It calls the CREATE procedure to create the disks.
The CREATE procedure makes sure that a disk is to be created. The only answer that will continue the process is “Yes.” Any other variation such as “Y” will not be accepted, to insure that a disk is not accidentally converted. After verifying that a disk is to be created, CREATE calls three procedures to do the work.

ZEROPASCALDIR zeros out the Pascal directory, erasing all files that were originally on the disk. It would be possible to write the program so that existing files are not lost, but the effort involved would probably not be worth it, since the disk to be converted will most likely be freshly formatted. In zeroing out the directory, the first 26 bytes are left undisturbed to preserve the volume name.
CREATEDOSFILE creates the DOS VTOC and directory. Creating the VTOC consists of setting various bytes to constant values and creating the free space map so that DOS will not use the Pascal area of the disk.

Creating the DOS directory is simple…

Creating the directory is relatively simple; it involves simply setting sectors 1 through 15 to zero and then linking them together. When the track is ready to be written, the program uses UNITWRITE for simplicity, instead of opening DOS-3.3- FILES, writing to the file with BLOCKWRITE, and closing the file. The program hopefully has all of the comments necessary so that its basic operation can be understood. The comments may be omitted to make the program easier to enter, but if the program is to be modified, the extra typing effort could be worthwhile. If you do not have any way to type the braces or underline character, the left and right braces can be replaced with “(” and “)” respectively, and the underline may be omitted since the compiler ignores them.
Using a technique similar to this, it should be possible to create a file that both Pascal and DOS can read without running a special conversion program first. Such a file could be combined with a “bilingual” disk to create systems consisting of both Pascal and BASIC programs sharing

… two-way files are the next logical step…

the same data files. Such two-way files are the next logical step in this project, although creating them would not be as simple as creating two-way disks. Another extension would be to create disks for use between CP/M and either Pascal or DOS, but at the present time there is not enough information on the CP/M disk format available.
This program was written as a part of a project directed by Dr. Donald Crouch as a part of the Computer Based Honors Program at the University of Alabama. The CBH program involves one year of accelerated programming classes followed by three years of projects assisting members of the faculty in applying the computer to research projects in many fields.

Program Listing
Name : BILINGUAL.TEXT

Name : BILINGUAL.TEXT


program bilingual;

{***********************************************************************}
{                                                                       }
{ BILINGUAL by Mat Davis, 17-Jan-82                                     }
{                                                                       }
{ This program will create a bilingual disk that can be used by both    }
{ Pascal and DOS 3.3 without conflict. Must be used only on Pascal      }
{ formatted disks, since the Pascal directory would overwrite the DOS   }
{ system tracks on a DOS-formatted disk.                                }
{                                                                       }
{***********************************************************************}

const
  eraseol=29; { Ctrl-] }
  eraseos=11; { Ctrl-K }

type
  byte=0..255;
  set_of_char=set of char;

var
  cmd: char;

{***********************************************************************}
{                                                                       }
{ GETCMD                                                                }
{                                                                       }
{ Get a character in OK into CMD                                        }
{                                                                       }
{***********************************************************************}

procedure getcmd(ok: set_of_char);

  begin { getcmd }
    repeat
      read(keyboard,cmd);
      if cmd in ['a'..'z'] then cmd:=chr(ord(cmd)-32);
    until cmd in ok;
  end;  { getcmd }

{***********************************************************************}
{                                                                       }
{ WRITE_DOS_DIR                                                         }
{                                                                       }
{ Create a DOS directory and put it on the disk                         }
{                                                                       }
{***********************************************************************}

procedure write_dos_dir;

  var
    dos_dir: array [0..15] of packed array [0..255] of byte;
    trans: array [0..15] of integer;
    i: integer;
  
  begin { write_dos_dir }
    gotoxy(8,0);
    write(chr(eraseos),'Writing DOS directory...');
    { Set up sector translation table }
    trans[0]:=0;
    for i:=1 to 14 do
      trans[i]:=15-i;
    trans[15]:=15;
    { Zero the dir }
    fillchar(dos_dir,sizeof(dos_dir),chr(0));
    { Set up VTOC }
    dos_dir[0][ 1]:= 17; { Directory track              }
    dos_dir[0][ 2]:= 15; { Directory sector             }
    dos_dir[0][ 3]:=  3; { DOS version number           }
    dos_dir[0][ 6]:=  1; { Disk volume number           }
    dos_dir[0][39]:=122; { Track/Sector pairs           }
    dos_dir[0][48]:= 17; { Last allocation              }
    dos_dir[0][49]:=255; { Allocation direction         }
    dos_dir[0][52]:= 35; { Tracks/Disk                  }
    dos_dir[0][53]:= 16; { Sectors/Track                }
    dos_dir[0][55]:=  1; { Bytes/Sector (High byte)     }
    { Make free space map }
    dos_dir[0][56]:=128; { Track 0, Sector 15           }
    dos_dir[0][57]:= 14; { Track 0, Sectors 1..3        }
    for i:=60 to 123 do
      dos_dir[0][i]:=255;
    { Link directory sectors }
    for i:=15 downto 2 do begin
      dos_dir[trans[i]][1]:=17;
      dos_dir[trans[i]][2]:=i-1;
    end; { for }
    { Write it to disk }
    unitwrite(5,dos_dir,4096,136);
  end;  { write_dos_dir }

{***********************************************************************}
{                                                                       }
{ CREATE_DOS_FILE                                                       }
{                                                                       }
{ Create the "bad" file for the DOS VTOC, directory, and files          }
{                                                                       }
{***********************************************************************}

procedure create_dos_file;

  var
    dir_buffer: packed array [0..511] of byte;
    f: file;
    junk: integer;
  
  begin { create_dos_file }
    gotoxy(8,0);
    write(chr(eraseos),'Creating DOS file...');
    
    { Create DOS_3.3_FILES from block 6 through 143       }
    
    rewrite(f,'#5:DOS_3.3_FILES[138]');
    
    { Write to the last block so the operating system won't crunch  }
    { it when it is closed }
    
    junk:=blockwrite(f,dir_buffer,1,137);
    close(f,lock);
    
    { Read Pascal directory }
    
    unitread(5,dir_buffer,512,2);
    if dir_buffer[30]<>5 then begin
      writeln('ERROR - BYTE 30 <> 5');
      halt;
    end; { if }
    
    { Set type of DOS_3.3_FILES to bad }
    
    dir_buffer[30]:=1;
    
    { Write directory }
    
    unitwrite(5,dir_buffer,512,2);
  end;  { create_dos_file }

{***********************************************************************}
{                                                                       }
{ ZERO_PASCAL_DIR                                                       }
{                                                                       }
{ Clear the Pascal directory                                            }
{                                                                       }
{***********************************************************************}

procedure zero_pascal_dir;
    
  var
    dir_buffer: packed array [0..2047] of byte;
  
  begin { zero_pascal_dir }
    gotoxy(8,0);
    write(chr(eraseos),'Zeroing directory...');
    
    { Load it... }
    
    unitread(5,dir_buffer,2048,2);
    
    { zero it... }
    
    fillchar(dir_buffer[16],2048-16,chr(0));
    
    { and write it }
    
    unitwrite(5,dir_buffer,2048,2);
  end;  { zero_pascal_dir }

{***********************************************************************}
{                                                                       }
{ CREATE                                                                }
{                                                                       }
{ Create the disk                                                       }
{                                                                       }
{***********************************************************************}

procedure create;

  var
    answer: string;
    i: integer;
  
  begin { create }
    page(output);
    writeln('Create:');
    writeln;
    writeln(chr(7),'*** WARNING ***',chr(7));
    writeln('This will lose ALL Pascal files that');
    writeln('  are on the disk.');
    write('Continue? (Yes/No) ');
    readln(answer);
    
    { Convert ANSWER to upper case }
    
    if length(answer)>0 then
      for i:=1 to length(answer) do
        if answer[i] in ['a'..'z'] then
          answer[i]:=chr(ord(answer[i])-32);
    if answer<>'YES' then exit(create);
    gotoxy(0,1);
    writeln(chr(eraseos));
    writeln('Put Pascal disk in unit #5');
    write('Press <SPACE> to create');
    getcmd([' ']);
    zero_pascal_dir;
    create_dos_file;
    write_dos_dir;
    gotoxy(8,0);
    writeln(chr(eraseos));
    writeln;
    writeln('Bilingual disk created');
  end;  { create }

begin { bilingual }
  repeat
    gotoxy(0,0);
    write(chr(eraseol),'Bilingual: C(reate, Q(uit [1.0]');
    getcmd(['C','Q']);
    if cmd='C' then create;
  until cmd='Q';
end.  { bilingual }

--- End of Program Listing ---
Please follow and like us:
error

About the Author