[Back to TUTOR SWAG index]  [Back to Main SWAG index]  [Original]

                         Turbo Pascal for DOS Tutorial
                             by Glenn Grotzinger
        Part 10 -- binary files; units, overlays, and include files.
             All parts copyright 1995-6 (c) by Glenn Grotzinger.

        There was no prior problem, so lets get started...

Typed binary files
==================
We know that files can be of type text.  We can also make them type "file
of <datatype>".  We can read and write binary data types to disk.  Here's
an example.   Keep in mind that with typed binary files, you can only
read and write the type of file you define it to be.  For the example
below, we can only deal with integers with this file.  The type we may
use may be anything that we have covered up to this point.  We also
will see that reading, accessing and writing of typed binary files will
be no different than accessing text files, except we can not make use
of readln and writeln (as those are for text files only).

program integers2disk;
{ writing integers 1 thru 10 to a disk data file, then reading 'em back }
   var
     datafile: file of integer;
     i: integer;
   begin
     assign(datafile, 'INTEGERS.DAT');
     rewrite(datafile);
     for i := 1 to 10 do
       write(datafile, i);
     close(datafile);  { done with write }
     reset(datafile);  { now lets start reading }
     read(datafile, i);
     while not eof(datafile) do { we can use the same concept }
       begin
         writeln(i);
         read(datafile, i);
       end;
     writeln(i);
     close(datafile);
   end.

You will notice the numbers 1 through 10 come up.  Look for the file
named INTEGERS.DAT, and then load it up in a text editor.  You will
notice that the file is essentially garbage to the human eye.  That,
as you see, is how the computer sees integers.  In part 11, I will
explain storage methods of many many different variables, and introduce
a few new types of things we can define.  We can use records, integers,
characters, strings, whatever...with a typed file as long as we comply
with the specific type we assign a file to be in the var line.

Untyped Binary Files
====================
We can also open binary files as an untyped, unscratched (essentially)
file.  There we simply use the declaration "file". (I think this is ver7
dependent, am I right?)  Anyway, in addition to this, we have to learn
a few new commands in order to use untyped files.

BLOCKREAD(filevar, varlocation, size of varlocation, totalread);

filevar is the untyped file variable.
varlocation is the location of where we read the variable into.
size of varlocation is how big varlocation is.
totalread is how much of varlocation that was readable. (optional)

BLOCKWRITE(filevar, varlocation, totalread, totalwritten);

filevar is the untyped file variable.
varlocation is the location of where we read the variable into.
totalread is how much of varlocation was readable. (optional)
totalwritten is how much of varlocation that was written. (optional)

SizeOf(varlocation)

Function that gives total size of a variable in bytes.

Maximum readable by BlockRead: 64KB.

Reset and Rewrite have a record size parameter if we deal with an untyped
file.

Probably, the best thing to make things clearer is to give an example.
This program does the same thing as the last one does, but only with
an untyped file.  See the differences in processing...

program int2untypedfile;

  var
    datafile: file;
    i: integer;
    numread: integer;

  begin
    clrscr;
    assign(datafile, 'INTEGERS.DAT');
    rewrite(datafile, 1);
    for i := 1 to 10 do
      blockwrite(datafile, i, sizeof(i));
    close(datafile);
    reset(datafile, 1);
    blockread(datafile, i, sizeof(i), numread);
    while numread <> 0 do
      begin
        writeln(i);
        blockread(datafile, i, sizeof(i), numread);
      end;
    close(datafile);
  end.
      
This program performs essentially the same function as the first example
program, but we are using an untyped file.  Blockread and blockwrite are
used in very limited manners here.  It's *VERY GOOD* for you to experiment
with their use!!!!!!!   As far as the EOF goes on a comparison, blockread
returns how many records it actually read.  We use that as an equivalent.

The 2 missing DOS file functions
================================
We now have the tools to perform the 2 missing DOS file functions that you
probably recognized were gone from part 8, copying files, and moving files.

Copying files essentially, is repeated blockreads and blockwrites until
all the input file is read and all the output file is written.  We can
do it with either typed or untyped files.  An untyped file example may
be found on page 14 of the Turbo Pascal 7.0 Programmer's Reference.

Moving files is a copy of an input file to a new location, followed by
erasure of the input file.

Units
=====
A unit is what you see probably on your drive in the TP/units directory.
Compiled units are TPU files.  They are accessed via USES clauses at the
start.  CRT, DOS, and WinDos are some of the provided units we have already
encountered.  Nothing is stopping us from writing our own, though.  The
actual coding of procedures/functions that we place into units is no
different.  The format of the unit, though, is something we need to think
about.  An example is the best thing for that.  This is a simple
implementation of a unit, with examples to give you some idea of a
skeleton to place procedures and functions into.

unit myunit;

  interface
     { all global const, type, and var variables go here as well as any
       code we may want to run as initialization for starting the unit. }

     { procedures and function headers are listed here }

     procedure writehello(str: string);

  implementation
     { actual procedural code goes here, as it would in a regular program }

     procedure writehello(str: string);  { must be same as above }
       begin
         writeln(str);
       end;

  end.

The unit up above is compilable to disk/memory, but unrunable.  Essentially,
what it is is a library of procedures/functions that we may use in other
programs.  Let's get an example out on how to use one of our own units.

program tmyunit; uses myunit; { must match ID at beginning }
  var
    str: string;
  begin
    str := 'Hello!  Courtesy of myunit!';
    writehello(str);
  end.

Though this program/unit combination is ludicrous, it does illustrate
exactly how to incorporate your own unit with MANY functions into your
programming, if your project gets too big, or for portability's sake
on some of your frequently used procedures.

Overlays
========
This will describe how to use TP's overlay facility.  It must be used with
units.  Typically, my thoughts are that if you get a large enough project
to dictate the use of overlays (we can use 'em on anysize projects, but
the memory taken up by the overlay manager far uses more memory on smaller
projects to make it an advantage to habitually do this).  We will use
the overlay facility with the unit/program set above for example purposes.

ONLY CODE IN UNITS HAVE AN OPPORTUNITY TO BE OVERLAID!  System, CRT, Graph,
and Overlay (if I remember right) are non-overlayable.

{$O+} is a compiler directive for UNITS only which designate a unit which
is OK to overlay.  {$O-} is the default, which says it's not OK to overlay
a unit.

To get to the overlay manager, we must use the overlay unit.

After the overlay unit, we need to use the {$O <TPU name>} compiler
directive to specify which units that we want to compile as an overlay.

WARNING: It is good to check your conversion to overlays in a program
with a copy of your source code.  If you alter it with overlays in mind
and it doesn't work (it's known to happen -- a procedure works ONLY when
it's not overlaid...), you won't have to go through the work to alter
it back if it doesn't work right...

NOTE: You must compile to disk, then run when you work with overlays.

Results come back in the OvrResult variable.  Here's a list...

                  0       Success
                 -1       Overlay manager error.
                 -2       Overlay file not found.
                 -3       Not enough memory for overlay buffer.
                 -4       Overlay I/O error.
                 -5       No EMS driver installed.
                 -6       Not enough EMS memory.

As for examples, let's look at the unit set up to overlay.  As we can
see, the only real difference (which is a good policy to make), is that
there is the {$O+} compiler directive there now...

{$O+}
unit myunit;

  interface
     { all global const, type, and var variables go here as well as any
       code we may want to run as initialization for starting the unit. }

     { procedures and function headers are listed here }

     procedure writehello(str: string);

  implementation
     { actual procedural code goes here, as it would in a regular program }

     procedure writehello(str: string);  { must be same as above }
       begin
         writeln(str);
       end;

  end.

Now lets look into the program itself.  It's error-reporting from the
overlay manager isn't great.  It stops the program if the overlay won't
load, but doesn't do a thing, really, with the ems section.

program tmyunit; uses myunit, overlay;

  {$O MYUNIT.TPU}        { include myunit in the overlay }
  var
    str: string;
  begin
    ovrinit('TMYUNIT.OVR'); { final overlay file name/init for program. }
    if OvrResult <> 0 then
      begin
        writeln('There is a problem');
        halt(1);
      end
    else
      write('Overlay installed ');
    ovrinitems;        {init overlay for EMS.  Usable after ovrinit}
    if OvrResult <> 0 then
      writeln('There was a problem putting the overlay in EMS')
    else
      writeln('in EMS memory.');
    str := 'Hello!  Courtesy of myunit!';
    writeln;
    writehello(str);
  end.

EXE Overlays
============
Here's how to set up EXE overlays.  The DOS copy command features the B
switch.  For example, to take the programs source file above and attach the
overlay to the end of the EXE (be sure you run any exe packers/encryptors
before you do this!), use the following:

        COPY /B TMYUNIT.EXE+TMYUNIT.OVR

Then the change that needs to be made in the source for the program is to
change the overinit line to read TMYUNIT.EXE instead of TMYUNIT.OVR.  You
should be able to handle doing this and understanding what is going on.

Include Files
=============
Use the {$I <filename>} compiler directive at the position the include
file is to be placed.  An include file is code that is in another file,
which may be considered as "part of the program" at the position the
{$I ..} compiler directive is at.

Copy function
=============
You can use the copy function to get a portion of a string into another
part of a string.  For example...

        str := copy('TurboPascal', 5, 3);
        writeln(str);      { writes oPa }

Programming Practice for Part #10
=================================
Write a copy equivalent which will copy one file from a given source to
a given destination.  Be sure it works with any accepted DOS combination
of statements.  Make the display of the program look something like this.

copying README.TXT (32,212 bytes)...done.

Code as much of this program up into a unit as possible, and make the unit
into an overlay.  Write the done when the file copies...Do not bother to
support any software switches.  Be sure the file is an exact copy, meaning
the date and time are identical as well as the file attributes.

Next Time
---------
Interfacing with a common format; how data types are stored in memory and
on disk.  You may wish to obtain use of a hex viewer for this next part.
Send comments to ggrotz@2sprint.net.



[Back to TUTOR SWAG index]  [Back to Main SWAG index]  [Original]