

                        Ŀ
                                          
                            CHAPTER II    
                                          
                        


           Ŀ
                                                        
                    THE STANDARD INTERRUPT 13h          
                                                        
           



1. GENERALITIES

   A large variety of disk-access services is available under MS-DOS
(via  INT 21h functions) to assembly language programmers.
Roughly speaking, the Turbo Pascal disk related facilities and the
INT 21h disk services are equivalent.
With very few exceptions the INT 21h services (for assembly language
programmers) or the Turbo Pascal build-in disk facilies are best
suited for disk standard operations such as handling files.

When it comes to examine diskettes, to repair them and to spot
abnormalities used for copy protection, lower level techniques are
often required. Interrupt 13h provides useful tools. Its procedures
reside in ROM.
At first glance, its capabilities seem rather poor. Wait and see !

I do not claim that the standard ROM based INT 13h is THE disk
problem solver. In fact, the next chapters will be devoted to show
how to improve its capabilities.
One thing at a time !

Usually, while writing a program, one has not to worry about where the
global variables are located in memory when the program is executed.
Although INT 13h is not too fastidious, it requires some care about
disk data transfer areas (buffers). This is because INT 13h uses a
capability of the system known as Direct Memory Access (DMA) and has
to comply with the DMA requirements.
To explain the restriction imposed by the DMA we need some terminology.

Page 0 of memory is the 64K-segment starting at the (absolute) address
       0000h:0000h  and ending at the (absolute) address 0000h:FFFFh

----------------------------- boundary -------------------------------

Page 1 of memory is the 64K-segment starting at the (absolute) address
       1000h:0000h  and ending at the (absolute) address 1000h:FFFFh

----------------------------- boundary -------------------------------

Page 2 of memory is the 64K-segment starting at the (absolute) address
       2000h:0000h  and ending at the (absolute) address 2000h:FFFFh

----------------------------- boundary -------------------------------

......................................................................


The restriction imposed by the DMA is :

            Ŀ
                                                        
              The buffer may NOT cross a page boundary  
                                                        
            

For example, a 20-byte buffer starting at the abslute address
0000:FFF0h is not allowed (it would start in page 0 and end in page 1).



2. INT 13h FUNCTIONS

   A total of six different functions are available through INT 13h
to access diskettes. They are :

          function 0  :  reset diskette system
          function 1  :  get diskette system status
          function 2  :  read sectors
          function 3  :  write sectors
          function 4  :  verify sectors
          function 5  :  format a track

Various registers are used to call INT 13h  (some functions do not use
all the registers shown in the following description) :



                  Register contents to call INT 13h

    Ŀ
      Reg                       contents                     
    Ĵ
      AH    function number (0..5)                           
    Ĵ
      AL    number of sectors to be read/written             
            (1..9  for standard diskettes)                   
    Ĵ
      ES    segment of buffer                                
    Ĵ
      BX    offset of buffer                                 
    Ĵ
      CH    track number  (normally  0..39)                  
    Ĵ
      CL    sector number  (1..9  for standard diskettes)    
    Ĵ
      DH    diskette side (0 or 1)                           
    Ĵ
      DL    drive number  (0 = drive A:   1 = drive B: )     
    



All INT 13h calls return

      CF = 0     AH = 0               if there was no error
      CF = 1     AH = error code      if there was an error

See Hogan's Sourcebook (4.041) for information about INT 13h error
codes.

The contents of AH returned by INT 13h functions is also stored at the
absolute address  0000:0441h .



3. CALLING INT 13h FROM TURBO PASCAL

   In assembly language, it is quite simple to load registers with
values and to call an interrupt. Turbo Pascal provides an easy method
(close to assembly language) to execute an interrupt.
It uses the DOS unit (in TURBO.TPL) and a REGISTERS-type variable
(REGISTERS is defined in the interface section of the DOS unit).

Here is an example:


program example_int13h;
uses dos;

var
regs:registers;
result:byte;
buffer:array[0..buffer_size-1] of byte;        (* see remark below *)
.......

.......................................

          with regs do
          begin
            ah:=function_number;
            al:=number_sectors;
            es:=seg(buffer);
            bx:=ofs(buffer);
            ch:=track_number;
            cl:=sector_number;
            dh:=side;
            dl:=drive;
          end;

          intr($13,regs);

 (* to move the error code into  result : *)

          result:=mem[0:$441];

.......................................
.......................................
.......................................

end.


Remark : buffer may not cross a page boundary.
I confess, I often use a quick and dirty trick ; I declare buffer to
reside at a specific address in memory :
         buffer:array[0..buffer_size-1] of byte absolute $7000:0000;
(when writing small programs which do not involve pointers).
There are, of course, much better ways :
If buffer_size is  nn  , one could declare a 'large' buffer :
         buffer:array[0..2*nn-1];
and use only a part of it (to be determined by the program at
runtime).
Other methods involving heap management are also available.


4. FUNCTIONS 0 AND 1

   We shall never use function 1 (get diskette system status) because
it does not do much more than to move the contents of memory location
0000:0441h  into AL.


On the contrary, function 0 (reset diskette system) is very important.
It resets the disk controller and recalibrates the drive's read/write
head.
Function 0 should always be called after any failed read, write,
verify or format request to ensure that the error-causing condition is
cleared in the controller.
Function 0 only uses AH ( = 0 )  and   DL.
From now on, we assume that all Turbo Pascal programs calling INT 13h
include a procedure RSET (we may not use  RESET  because it is a
reserved word) :

procedure rset;
begin
regs.ah:=0;             (*  just as in the example in section 3,  *)
regs.dl:=drive;         (*  regs is a registers-type variable     *)
intr($13,regs);
end;


After any other Int 13h drive operations, something like :

error:=mem[0:$441];            (*  save error code           *)
if (error <> 0) then rset;     (*  error=0  means  no error  *)

should be executed.



5. FUNCTIONS 2..4

   From a programming point of view, function 2 (read sectors),
function 3 (write sectors) and function 4 (verify sectors) are all
similar. They use all registers shown in section 2.

The verify operation is identical to the read operation, except that
the data are thrown away rather than stored into memory.
Nevertheless it requires  ES:BX  to be initiazed properly (no page
boundary crossing). As no buffer is really needed, an absolute
declaration such as
               buffer:byte absolute $7000:0000;
will not cause any problem.
The verify operation is often used to try spotting bad areas on the
diskette.

The read operation and the write operation are data transfer
operations performing exactly what one expects.

Read, write and verify operations may involve several sectors.
For example, if the read function is called with  AL=3  CL=4
sectors  4 to 6  will be read consecutively. This means that the
buffer should be (at least) of size  512 * 3 = 1536 (for standard
diskettes).
If we want to read, write or verify more than one sector, every sector
should be on the same track and on the same side. In other words, (for
standard diskettes) :     1  <=  AL  <=  9 - CL + 1  .

As an error may appear because the drive motor has not reached a
working speed at the time of the request, it is recommended to try
read, write and verify operations three times before assuming an error
is real (and, of course, to use the reset operation between tries).

EXERCISE  Write a program that verifies all sectors on a diskette.
          [Hint : first write a procedure   verify(t,h,s,n:byte)  that
verifies  n  sectors starting with sector  s  on track t / head h ].
Such a program will turn out to be useful to spot bad sectors and non
standard sectors.



6. FUNCTION 5

   Function 5 formats  * one track *  on  * one side *  of a diskette.
All registers shown in section 2 are used except AL and CL.
The format operation writes organizational bytes (see Chapter I -
section 7) as well as data bytes (all data bytes are identical).
One may wonder why a buffer (pointed to by ES:BX) is required to
perform a format operation. In fact the buffer must hold the  CHRN
4-byte fields written on the track.
So, for a standard track, 36 bytes are required and one can use :

for i:=0 to 8 do
begin
buffer[4*i]:=track;
buffer[4*i+1]:=head;
buffer[4*i+2]:=i+1;
buffer[4*i+3]:=2;
end;

In fact the format operation  * does not check anything *  about the
values held in the buffer.
One can take advantage of this 'weakness' to write anything one likes
in the CHRN fieds. Of course, sophisticated techniques are required
when it comes to read/write sectors with strange CHRN fields.

Some copy protection methods format a track with nine identical CHRN
fields and write nine different data fields. It is obvious that it is
rather difficult to copy such a diskette.

If a track (say track 10 / head 0) is formatted with the following
CHRN fields
                  10   0   1   2
                  10   0   2   2
                  10   0   3   2
                  10   0   4   2
                  10   0   5   2
                  10   0   6   2
                  10   0   7   2
                  10   0   9*  2      *  non standard
                  10   0   8*  2      *  order

and all other tracks are formatted in the standard way, DISKCOPY will
not realize that the diskette is non standard and will agree to copy
to a standard destination disk. DISKCOMP will display : 'diskettes
compare ok ' .
This is an example of a quite simple (though 'cruel') copy protection
method.

We will learn how to read CHRN fields in Chapter III.



7. THE DISK BASE TABLE

   One can raise many questions that are unanswered in the previous
sections. For example, how does the format operation know that nine
CHRN fields are to be written on a track?

The  * dword *  at memory location  0000:0078h  is a pointer to the
11-byte Disk Base Table.
The overall operation of INT 13h is controlled by parameters in this
table.

                           Disk Base Table

    Ŀ
     offset                   descriptions                     
    Ĵ
      00h     first specify byte                               
    Ĵ
      01h     second specify byte                              
    Ĵ
      02h     wait time until motor turned off                 
    Ĵ
      03h     number of bytes per sector code                  
    Ĵ
      04h     number of sectors per track                      
    Ĵ
      05h     read/write gap length                            
    Ĵ
      06h     data length when sector length not specified     
    Ĵ
      07h     format gap length                                
    Ĵ
      08h     fill byte for formatting                         
    Ĵ
      09h     head-settle time                                 
    Ĵ
      0Ah     motor start-up time                              
    


The first specify byte, the second specify byte, the head-settle time
and the motor start-up time are 'technology' bytes. They should NEVER
be changed.

The wait time until motor turned off parameter specifies how long the
diskette motor is to be left running after each operation (the motor
is left on in case the diskette is needed again). The value is in
units of clock ticks (about 1/18.2 second). We will never need to
change this parameter.

The number of bytes per sector code  is used by INT 13h to determine
the actual number of data bytes per sector. If the code is  n  then
the actual number of data bytes per sector is   128 * 2^n.
The standard code  is  2  (i.e.  512  data bytes).  This parameter
often needs to be changed for non standard diskettes.
Strange things may happen when the code is  0  (see Appendix C :
'The Strange Case of N = 0').

For non standard diskettes, the number of sectors per track may be
different from the normal value 9.

The format gap length (normal value : 80 = 50h) is the number of bytes
of 4Eh written between sectors when a track is formatted (see the fine
structure of tracks in Chapter I - section 7).
It does not seem safe to use a value smaller than 12.

The read/write gap length is used when reading or writing. It tells
INT 13h how long to wait before looking for the next sector so that it
can avoid reading some of the (nonsense) bytes of 4Eh in the gap
between sectors.
The normal value is 42 = 2Ah.
Changing the read/write gap length to 8 will usually produce good
results even for non standard diskettes.
Obviously, the read/write gap must be smaller than the format gap.

We shall never need to change the value (255 = FFh) of the parameter
'data length when sector length not specified'. See Appendix C :
'The Strange Case of N = 0'.

The fill byte for formatting provides the data value that is stored
in each data byte of the sectors when a track is formatted.
It can be changed to anything.




8. CHANGING ENTRIES IN THE DISK BASE TABLE

   It is not difficult to change entries in the Disk Base Table. Good
programming practice requires that all entries are restored to their
original value before a program terminates (even in case of
termination due to a runtime error). This requirement implies that the
original values should be stored somewhere.

The following program demonstrates how to work out things in Pascal
(the reader is referred to TURBO PASCAL OWNER'S HANDBOOK for
information about exit procedures).


program ........;
................
var
table_seg,table_ofs:word;           (*  to store table address    *)
copy_table:array [0..10] of byte    (*  to store original values  *)
exit_save:pointer;
.................
.................

(* $F+ *)                           (*  compiler directive        *)
procedure restore_table;
begin
for i:=0 to 10 do mem[table_seg:table_ofs + i]:= copy_table[i];
exitproc:=exit_save;
end;
(* $F- *)                           (*  compiler directive        *)


...............................
...............................


             (* to change byte at offset  k  in table : *)

             mem[table_seg:table_ofs + k]:=......;


...............................
...............................



begin                            (*  main       *)

table_seg:=memw[0:$7a];          (*  get table  *)
table_ofs:=memw[0:$78];          (*  address    *)

for i:=0 to 10 do copy_table[i]:=mem[table_seg:table ofs + i];

                                 (*  save original table  *)

exit_save:=exitproc;
exitproc:=@restore_table;

........................
........................


end.



9.  PLAYING TRICKS WITH INT 13h

    We are now ready to experiment with some non standard diskettes
(using a scratch disk !!!).
We shall give two examples.


EXAMPLE 1 : (a) We format track 10 / head 0 with the following CHRN
                fields

             10  0  1  1
             10  0  R  2     (2  <= R  <=9)

(using the standard Disk Parameter Table).

(b) We read  track 10 / head 0 / sector 2  using the standard Disk
    Parameter Table. The error code should be  00  (no error).

(c) We read  track 10 / head 0 / sector 1  using the standard Disk
    Parameter Table. The read operation shows an error code  04
(meaning : sector not found).
The read operation first tries to locate the sector by reading CHRN
fields (until the right one is found or until all have been read).
In the present case, it is looking for  C = 10   H = 0   R = 1  (as
requested)  and   *  N = 2  *  (the value of byte 3 in the Disk
Parameter Table; it is not possible to pass a sector length code
through the registers shown in section 2).

(d) Let us change byte 3 of the Disk Parameter Table (number of bytes
    per sector code)  into  01  and try reading again  track 10 /
head 0 / sector 1.
The read operation now shows an error code  10h  (meaning :
CRC error).
This is because the track has been formatted under control of the
standard Disk Base Table and therefore each sector contains 512 data
bytes. When we read  track 10 / head 0 / sector 1  using our modified
Disk Parameter Table, only   128 * 2^1 = 256  bytes are read.
Of course, reading the first 256 bytes out of 512 is not a real
problem! We need to remember that a CRC word is written after the data
bytes when a write or format operation is executed. The CRC (word) is
a mathematical function (to be discussed in Appendix A) of the data
bytes  * and *  of the number of data bytes.
In our case, the disk controller reads the first 256 bytes and
interprets the two next bytes as a CRC; it tells INT 13h that a bad
CRC is detected.

(e) Using the same Disk Parameter Table as in (d), let us write any
    256 bytes we like to  track 10 / head 0 / sector 1 .
The write operation should show an error code  00  (no error).
A subsequent read operation shows an error code  00.
This is because the rewriting operations does not only updates data
byte; the disk controller also synthetizes a new, correct CRC.



EXAMPLE 2 : (a) Starting with a standard Disk Base Table, we change
                its byte 4 (number of sector per track) to 11.
We format  track 10 / head 0 with the following CHRN fields

                    10  0  R  2    (1  <=  R  <=  11)  .

(b) Using the same Disk Base Table as in (a), we read  track 10 /
    head 0 / sector 1 . The read operation shows an error  04
(meaning : sector not found).
If we try any other sector R with   2  <=  R  <=  10   the read
operation still shows an error  04 .
What about sector 11 ?
Surprisingly, no error is shown.
The explanation uses facts described in  Chapter 1 - sections 8 and 9.
The angular position of the CHRN field of sector 10 is
(162 + 9 * 654) * 36 / 625 = 348  and the angular position of the
CHRN field of sector 11 is  (162 + 6540) * 36 / 625 = 386 .
So, during one diskette complete revolution the first ten CHRN fields
are written to the disk.
The 574 bytes for sector 11 are written while the diskette is
performing a  * second *  revolution. After these 574 bytes, bytes of
4Eh are written  * to the end *  of the track overwritting previous
disk bytes!



NOTE : When one tries to read a sector  R  and

              R  >  number of sectors per track  (byte 4 of the Disk
                                                 Base Table)
problems may occur. For such cases it is often useful to change byte 4
of the Disk Base Table to  FFh.


REMARK : INT 13h should NEVER be used to try to read a sector whose
         CHRN field is such that

                C  <>  the real track number
         or     H  <>  the real head number .

More sophisticated techniques are required to attempt such readings.