home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / LOWDSK.ZIP / LOWDSK.PAS
Encoding:
Pascal/Delphi Source File  |  1987-11-08  |  11.2 KB  |  326 lines

  1. PROGRAM tst;
  2.     {-read or infer critical disk parameters}
  3.     {-check logical sector number calculations}
  4.   TYPE
  5.     drives = (A, B, C, D, E, F);
  6.     drivedescr = RECORD
  7.                    BIOSnum, DOSnum, IDbyte : Byte;
  8.                    sectors, tracks, heads, secSize,
  9.                    alloUnits, secsPerAllo, bootOffset : Integer;
  10.                  END;
  11.     drivearray = ARRAY[drives] OF drivedescr;
  12.     sectorbuffer = ARRAY[1..512] OF Char;
  13.     registers = RECORD
  14.                   CASE Integer OF
  15.                     1 : (ax, bx, cx, dx, bp, si, di, ds, es, flags : Integer);
  16.                     2 : (al, ah, bl, bh, cl, ch, dl, dh : Byte);
  17.                 END;
  18.   VAR
  19.     DR : drivearray;
  20.     reg : registers;
  21.     lastch, ch : Char;
  22.     sb : sectorbuffer;
  23.     curdrive : drives;
  24.     logsec, curhead, cursector, curtrack, error : Integer;
  25.  
  26.   PROCEDURE BIOSreadSectors(drive : Byte;
  27.                             sector, track, head : Integer;
  28.                             sects : Integer;
  29.                             VAR buffer : sectorbuffer;
  30.                             VAR error : Integer);
  31.       {-execute int 13 to read disk via BIOS at low level}
  32.     BEGIN
  33.       reg.ax := $200 OR sects;
  34.       reg.dl := drive;
  35.       reg.dh := head;
  36.       reg.ch := track AND 255;
  37.       reg.cl := (sector AND 63) OR ((track SHR 8) SHL 6);
  38.       reg.es := Seg(buffer);
  39.       reg.bx := Ofs(buffer);
  40.       Intr($13, reg);
  41.       IF Odd(reg.flags AND 1) THEN BEGIN
  42.         error := reg.ax SHR 8;
  43.         WriteLn('disk read error# ', error);
  44.       END ELSE
  45.         error := 0;
  46.     END;                      {biosreadsectors}
  47.  
  48.   PROCEDURE DOSreadSectors(drive : Byte;
  49.                            LSN : Integer;
  50.                            sects : Integer;
  51.                            VAR buffer : sectorbuffer;
  52.                            VAR error : Integer);
  53.       {-execute int 25 to read disk through DOS at low level}
  54.     BEGIN
  55.       INLINE(
  56.         $1E/                  {PUSH    DS}
  57.         $8A/$46/$10/          {MOV    AL,[BP+10]}
  58.         $8B/$56/$0E/          {MOV    DX,[BP+0E]}
  59.         $8B/$4E/$0C/          {MOV    CX,[BP+0C]}
  60.         $C5/$5E/$08/          {LDS    BX,[BP+08]}
  61.         $CD/$25/              {INT    25}
  62.         $72/$02/              {JB    0113}
  63.         $31/$C0/              {XOR    AX,AX}
  64.         $9D/                  {POPF    }
  65.         $1F/                  {POP    DS}
  66.         $5D/                  {POP    BP}
  67.         $C4/$7E/$04/          {LES    DI,[BP+04]}
  68.         $26/                  {ES:    }
  69.         $89/$05               {MOV    [DI],AX}
  70.         );
  71.     END;                      {int25}
  72.  
  73.   PROCEDURE DisplaySector(sb : sectorbuffer);
  74.       {-write the read-in sector to screen}
  75.     VAR
  76.       i : Integer;
  77.       ch : Char;
  78.     BEGIN
  79.       FOR i := 1 TO 512 DO BEGIN
  80.         IF sb[i] IN [#10, #13, #32..#127] THEN
  81.           Write(sb[i])
  82.         ELSE
  83.           Write('.');
  84.       END;
  85.       WriteLn;
  86.       Write('press any key...'); Read(Kbd, ch); WriteLn;
  87.     END;                      {displaysector}
  88.  
  89.   PROCEDURE GetDisk(drive : drives; VAR ddesc : drivedescr);
  90.       {-get the physical parameters of a drive}
  91.  
  92.     FUNCTION isBIOSnum(drive : drives) : Byte;
  93.         {-return the drive number that BIOS uses}
  94.       BEGIN
  95.         CASE Ord(drive) OF
  96.           0 : isBIOSnum := 0;
  97.           1 : isBIOSnum := 1;
  98.           2 : isBIOSnum := $80;
  99.           3 : isBIOSnum := $81;
  100.           4 : isBIOSnum := $82;
  101.           5 : isBIOSnum := $83;
  102.         ELSE
  103.           isBIOSnum := -1;
  104.         END;
  105.       END;                    {isbiosnum}
  106.  
  107.     FUNCTION isDOSnum(drive : drives) : Byte;
  108.         {-return the drive number that DOS uses}
  109.       BEGIN
  110.         IF Ord(drive) IN [0, 1, 2, 3, 4, 5] THEN
  111.           isDOSnum := Ord(drive)
  112.         ELSE
  113.           isDOSnum := -1;
  114.       END;                    {isdosnum}
  115.  
  116.     PROCEDURE getFAT(DOSnum : Byte; VAR driveid : Byte;
  117.                      VAR secSize, alloUnits, secsPerAllo : Integer);
  118.         {-read the FAT ID info for the specified drive}
  119.       BEGIN
  120.         reg.ah := $1C;
  121.         reg.dl := DOSnum+1;
  122.         MsDos(reg);
  123.         secSize := reg.cx;
  124.         alloUnits := reg.dx;
  125.         secsPerAllo := reg.al;
  126.         driveid := Mem[reg.ds : reg.bx];
  127.       END;                    {getfat}
  128.  
  129.     PROCEDURE GetDriveParams(BIOSnum : Byte;
  130.                              VAR heads, sectors, tracks : Integer);
  131.         {-read the BIOS drive parameters for the specified drive}
  132.       BEGIN
  133.         reg.ah := 8;
  134.         reg.dl := BIOSnum;
  135.         Intr($13, reg);
  136.         IF Odd(reg.flags AND 1) THEN BEGIN
  137.           WriteLn('error getting drive info for drive ', BIOSnum, '....');
  138.           Halt;
  139.         END;
  140.         heads := 1+reg.dh;
  141.         sectors := reg.cx AND 63;
  142.         tracks := 1+(reg.ch OR ((reg.cl SHR 6) SHL 8));
  143.       END;                    {getdriveparams}
  144.  
  145.     PROCEDURE ReadBootRecord(BIOSnum : Byte; VAR bootOffset : Integer);
  146.         {-determine the offset to use between LSNs and physical sectors}
  147.       TYPE
  148.         partitionrecord = RECORD
  149.                             bootind, bhead, bsector, bcyl : Byte;
  150.                             systind, ehead, esector, ecyl : Byte;
  151.                             relsectl, relsecth : Integer;
  152.                             numsectl, numsecth : Integer;
  153.                           END;
  154.         bootrecord = ARRAY[1..4] OF partitionrecord;
  155.       VAR
  156.         partition : Byte;
  157.         bootrec : bootrecord;
  158.       BEGIN
  159.         BIOSreadSectors(BIOSnum, 1, 0, 0, 1, sb, error);
  160.         IF error <> 0 THEN BEGIN
  161.           WriteLn('could not read master boot record');
  162.           Halt;
  163.         END;
  164.         {get the bootrecord out of the sector}
  165.         Move(sb[447], bootrec, 66);
  166.         {find the selected partition}
  167.         partition := 0;
  168.         REPEAT
  169.           partition := Succ(partition);
  170.         UNTIL (bootrec[partition].bootind = $80) OR (partition > 4);
  171.         IF partition > 4 THEN BEGIN
  172.           WriteLn('error, did not find a selected partition');
  173.           Halt;
  174.         END;
  175.         {calculate the bootoffset}
  176.         bootOffset := bootrec[partition].relsectl;
  177.         IF bootrec[partition].relsecth <> 0 THEN
  178.           WriteLn('unsupported disk: high word of relative sector offset not zero');
  179.       END;                    {readbootrecord}
  180.  
  181.     BEGIN                     {getdisk}
  182.       WITH ddesc DO BEGIN
  183.         BIOSnum := isBIOSnum(drive);
  184.         DOSnum := isDOSnum(drive);
  185.         {read the FAT information}
  186.         getFAT(DOSnum, IDbyte, secSize, alloUnits, secsPerAllo);
  187.  
  188.         IF (BIOSnum IN [0, 1]) THEN BEGIN
  189.           {a floppy, use FAT ID code to set values}
  190.           CASE IDbyte OF
  191.             $FF : BEGIN
  192.                     heads := 2; sectors := 8;
  193.                   END;
  194.             $FE : BEGIN
  195.                     heads := 1; sectors := 8;
  196.                   END;
  197.             $FD : BEGIN
  198.                     heads := 2; sectors := 9;
  199.                   END;
  200.             $FC : BEGIN
  201.                     heads := 1; sectors := 9;
  202.                   END;
  203.             $F9 : BEGIN
  204.                     heads := 2; sectors := 15;
  205.                   END;
  206.             $F8 : BEGIN
  207.                     WriteLn('hard drive unexpected for A or B....');
  208.                     Halt;
  209.                   END;
  210.           END;
  211.           tracks := 1+Trunc(Int(secsPerAllo*alloUnits)/Int(sectors*heads));
  212.           bootOffset := 0;
  213.         END ELSE IF (BIOSnum IN [$80, $81, $82, $83]) THEN BEGIN
  214.           {a hard drive, use BIOS function}
  215.           GetDriveParams(BIOSnum, heads, sectors, tracks);
  216.           {also need to get the boot offset between BIOS and DOS sectors}
  217.           ReadBootRecord(BIOSnum, bootOffset);
  218.         END ELSE BEGIN
  219.           {drive not known to BIOS}
  220.           WriteLn('drive unknown to BIOS ');
  221.           Halt;
  222.         END;
  223.       END;                    {with}
  224.     END;                      {getdisk}
  225.  
  226.   PROCEDURE ShowDisk(drive : drives);
  227.       {-display the physical parameters of a drive}
  228.     VAR
  229.       bytes : Real;
  230.       ch : Char;
  231.     BEGIN
  232.       WITH DR[drive] DO BEGIN
  233.         WriteLn;
  234.         WriteLn('drive: ', Chr(Ord(DOSnum)+65));
  235.         WriteLn('   BIOSnum    = ', BIOSnum);
  236.         WriteLn('   sectors    = ', sectors);
  237.         WriteLn('   tracks     = ', tracks);
  238.         WriteLn('   heads      = ', heads);
  239.         WriteLn('   secsize    = ', secSize);
  240.         WriteLn('   alloc units= ', alloUnits);
  241.         WriteLn('   alloc size = ', secsPerAllo);
  242.         WriteLn('   boot offset= ', bootOffset);
  243.         bytes := 1.0*sectors*tracks*heads*secSize;
  244.         WriteLn('   BIOS bytes = ', bytes : 8 : 0);
  245.         bytes := 1.0*alloUnits*secsPerAllo*secSize;
  246.         WriteLn('   DOS  bytes = ', bytes : 8 : 0);
  247.       END;
  248.       WriteLn;
  249.       Write('press any key...'); Read(Kbd, ch); WriteLn;
  250.       WriteLn;
  251.     END;                      {showdisk}
  252.  
  253.   FUNCTION LSN(drive : drives; sector, track, head : Integer) : Integer;
  254.       {-return the logical sector number for a set of physical parameters}
  255.     BEGIN
  256.       WITH DR[drive] DO
  257.         LSN := Pred(sector+sectors*(head+track*heads))-bootOffset;
  258.     END;                      {lsn}
  259.  
  260.   PROCEDURE PhySect(drive : drives; LSN : Integer;
  261.                     VAR sector, track, head : Integer);
  262.       {-return the physical drive parameters for a given lsn}
  263.     VAR
  264.       rem : Integer;
  265.     BEGIN
  266.       WITH DR[drive] DO BEGIN
  267.         {correct for the boot partitioning}
  268.         LSN := LSN+bootOffset;
  269.         track := LSN DIV (sectors*heads);
  270.         rem := LSN-track*sectors*heads;
  271.         head := rem DIV sectors;
  272.         rem := rem-head*sectors;
  273.         sector := Succ(rem);
  274.       END;
  275.     END;                      {physect}
  276.  
  277.   FUNCTION TotSectors(drive : drives) : Real;
  278.       {-return total number of sectors on drive}
  279.     BEGIN
  280.       WITH DR[drive] DO
  281.         TotSectors := 1.0*heads*tracks*sectors-1.0;
  282.     END;                      {totsectors}
  283.  
  284.   BEGIN
  285.     ch := 'X';
  286.  
  287.     {loop for testing}
  288.     WHILE True DO BEGIN
  289.       lastch := ch;
  290.       WriteLn;
  291.       Write('Enter drive letter to read (X to quit): ');
  292.       ReadLn(ch);
  293.       IF UpCase(ch) = 'X' THEN Halt;
  294.       curdrive := drives(Ord(UpCase(ch))-65);
  295.  
  296.       {initialize the drive descriptors}
  297.       IF UpCase(ch) <> UpCase(lastch) THEN BEGIN
  298.         GetDisk(curdrive, DR[curdrive]);
  299.         ShowDisk(curdrive);
  300.       END;
  301.  
  302.       {get a logical sector number}
  303.       Write('Enter logical sector number to read: (0..',
  304.       TotSectors(curdrive) : 0 : 0, '): ');
  305.       ReadLn(logsec);
  306.  
  307.       {calculate physical sector parameters}
  308.       PhySect(curdrive, logsec, cursector, curtrack, curhead);
  309.  
  310.       {read directly through BIOS}
  311.       WriteLn('BIOS RESULTS');
  312.       BIOSreadSectors(DR[curdrive].BIOSnum, cursector, curtrack, curhead, 1, sb, error);
  313.       IF error = 0 THEN DisplaySector(sb);
  314.  
  315.       {read through DOS low level facilities}
  316.       WriteLn;
  317.       {check calculation both ways}
  318.       IF logsec <> LSN(curdrive, cursector, curtrack, curhead) THEN
  319.         WriteLn('sector conversion error');
  320.       WriteLn('DOS RESULTS');
  321.       DOSreadSectors(DR[curdrive].DOSnum, logsec, 1, sb, error);
  322.       IF error = 0 THEN DisplaySector(sb);
  323.  
  324.     END;
  325.   END.
  326.