25 Years of Programming
An open source source for C, C++, OWL, BASIC, MDB, XLS, DOT, and more...
Home   Projects   Up   Sitemap   Search   Blog   Forum+Chat   About Us   Privacy   Terms of Use   Feedback   FAQ   Images   Services   Ads   Donate   Humor

Read absolute disk sectors

ReadSect.cpp reads absolute disk sectors using Borland C++ 4.0 DOS-only functions. The functions rely on MSDOS system calls, so this program can only read disk formats that MSDOS recognizes and supports.

Click here to download readsect.zip (17 KB). The zip file contains the same items that are listed on this web page:

  • Readsect.cpp. The program.
  • Drives.xls. Microsoft Excel 5.0 (or later) worksheet with some file allocation table (FAT) information and calculations. Screenshot is below.
  • Old96TPI.txt. A debug listing of the partial boot loader from a Heathkit H-100 96TPI disk, with its data items labeled. Listing is below.
  • Copying.txt. GNU GPL license.

The items in this project were part of an attempt to use a DOS 6.0 / Windows 3.1 computer to rescue data from 5 1/4" floppy disks from a Heathkit H-100 computer that were in 96 TPI (Track Per Inch) 640KB and 720KB formats. The attempt was unsuccessful.

If you are working on something for which you need the information in this project, you have my sympathy.

readsect.cpp

/*	readsect.cpp			12-11-99
	Copyright (C)1999 Steven Whitney.
	Published under GNU GPL (General Public License) Version 2, with ABSOLUTELY NO WARRANTY.
	Initially published by http://25yearsofprogramming.com.

	Reads absolute disk sectors using Borland C++ 4.0 MSDOS DOS-only functions.

	Although it only reads, you should nonetheless study and make sure you understand what this
	program does before you run it.
	
	This program was part of an unsuccessful attempt to use a DOS 6.0 / Windows 3.1 computer to
	rescue data from 5" floppy disks from a Heathkit H-100 computer that were in
	96 tpi (track-per-inch), 640KB and 720KB formats.
	
	Because these Borland functions use DOS calls, they still rely on the parameters in the DOS
	disk tables. They cannot bypass them. I also tried manually modifying the disk tables,
	but no combination of parameters worked. There may be tasks for which this program is useful,
	but getting data from disks that are not in one of the standard, currently supported,
	MSDOS disk formats is not one of them.

------
TO DO:

"Sector Not Found" errors: the sector header includes track/side/sectorno.
(with sectorno applying only to the current track)
you're searching for an exact match for all 3, and if your translation
routine (driver?) calculates the wrong combination and passes it
to the controller for the search, the sector won't be found, even if the sector
itself can be read ok.

8/13/05
If necessary, copy this to a new project and modify it for use on the P5-90 hard disk.
On a hard disk, a cylinder is the same as a track. All the heads are always simultaneously
on the same track of their respective platters; they move together.

------
NOTES:

*/
#include <bios.h>
#include <dos.h>
#pragma hdrstop

#include "c:\bcs\my.h"

#include "c:\bcs\mylib.cpp"

//////////////////////////////////////////////////////////////////////////////
// global data
//----------------------------------------------------------------------------

BOOL showsectordata;
char buffer[3 * 512];		// add 4 bytes for long read (516)

//----------------------------------------------------------------------------
// translate to viewable chars that won't mess up screen.
uchar MakePrintable(int ch)
{
ch &= 0x7F;						// strip parity
if((ch < 32) || (ch == 127))
	return('.'); 			    // show control codes as dots
return(ch);
}                  		//MakePrintable
//----------------------------------------------------------------------------
void diskstatus()
{
// 15 79 2 1 	// normal values reported for 5" drive
// try setting drive B: parms
// what, and how passed? in buffer?  note desc says "drive PAIR" parms.
// buffer[0] = 15; buffer[1] = 79; buffer[2] = 2; buffer[3] = 1;
// int result = biosdisk(9, 1, 1, 79, 9, 1, buffer);
// cout << "Result code: 0x" << hex << result << dec << endl;
// cout << endl;
//
// report drive characteristics
// B: is reported as 15 sectors even after drivparm sets it to 9 in config.sys.
for(int drive = 0 ; drive < 2 ; drive++)
{
	int result = biosdisk(8, drive, 0, 0, 1, 1, buffer);
	cout << "Result code: 0x" << hex << result << dec << endl;
	cout << "Drive " << (char)(drive + 'A') << ": characteristics: " << endl;
	for(int i = 0 ; i < 4 ; i++)
		cout << hex << "0x" << (uint)buffer[i] << dec << "  ";
	cout << endl;
	for(i = 0 ; i < 4 ; i++)
		cout << "  " << (uint)buffer[i] << "  ";
	cout << endl;
}
cout << endl;
}                  		//diskstatus
//----------------------------------------------------------------------------
// returns user keypress, which can be ESC to abort current process
int showsector()
{
for(int i = 0 ; i < 512; i++)
	cout << MakePrintable(buffer[i]);
cout << endl;
return(presskey());
}                  		//showsector
//----------------------------------------------------------------------------
// it cannot read an H100 96tpi disk beyond sector 9. (track 0 only)
int usebiosdisk()
{
int ch = 0;
cout << "Attempting to read from drive B: using BIOSDISK" << endl;
for(int track = 0 ; track < 80 ; track++)			// numbering starts at 0
{
	for(int sector = 1 ; sector < 10 ; sector++)	// numbering starts at 1
	{
		for(int head = 0 ; head < 2 ; head++)
		{
			int drive = 1;								// 1 = B:
			int result = biosdisk(2, drive, head, track, sector, 1, buffer);
			cout << "Track " << track << ", Sector " << sector << ", Head " << head << ":  ";
			if(result == 0)
			{
				cout << "OK!" << endl;
			}
			else
			{
				cout << "Failed: 0x" << hex << result << dec << endl;
			}
			if(showsectordata)
				ch = showsector();
			if(ch == ESC)
				break;
		}
		if(ch == ESC)
			break;
	}
	if(ch == ESC)
		break;
	if(kbhit())
	{
		getch();
		break;
	}
}
// hard controller reset, just in case?  (untested, might reset computer? or crash?)
// result = biosdisk(0, 1, 0, 0, 1, 1, buffer);
return(1);
}                      	//usebiosdisk
//----------------------------------------------------------------------------
// H100 disk using normal drivparms: no.
// H100 disk using modified drivparms:  read 18 sectors, then errors,
// then some more sectors, then all errors thereafter.
// absread changes data at 70:34e to 54e, biosdisk doesn't.
int useabsread()
{
int ch = 0;
cout << "Attempting to read from drive B: using ABSREAD" << endl;
for(long sector = 0 ; sector < 1440 ; sector++)			// numbering starts at 0
{
	int drive = 1;								// 1 = B:
	int result = absread(drive, 1, sector, buffer);

// 	cout << "Track " << track << ", Sector " << sector << ", Head " << head << ":  ";
	cout << "Sector " << sector << ":  ";
	if(result == 0)
	{
		cout << "OK!" << endl;
	}
	else
	{
		cout << "Failed: 0x" << hex << errno << dec << endl;
	}
	if(showsectordata)
		ch = showsector();
	if(kbhit() || (ch == ESC))
		break;
}
return(1);
}                   	//useabsread
//----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
int main()
{
string::set_case_sensitive(0);
string::set_paranoid_check(1);

clrscr();
cout << setiosflags(ios::uppercase);

cout << "Insert disk in drive B:" << endl;
presskey();

while(1)
{
	int choice;
	do
	{
		cout << endl;
		cout << "-----------------------------------------------" << endl;
		cout << "0  Exit" << endl;
		cout << "1  Disk status report" << endl;
		cout << "2  Use BIOSDISK to read B:" << endl;
		cout << "3  Use ABSREAD to read B:" << endl;
		cout << "4  DIR B:" << endl;
		cout << "5  B: FAT ID and structure info" << endl;
		cout << endl << "Enter choice: ";
		cin >> choice;
		cout << endl;
	}
	while((choice < 0) || (choice > 5));
	switch(choice)
	{
		case 0:
			return(0);
		case 1:
			diskstatus();
			break;
		case 2:
			showsectordata = yesno("Show sector data");
			cout << "Press ESC to quit" << endl;
			usebiosdisk();
			break;
		case 3:
			showsectordata = yesno("Show sector data");
			cout << "Press ESC to quit" << endl;
			useabsread();
			break;
		case 4:
			system("dir b:");
			break;
		case 5:
		{
			// getfat() uses DOS function 1C (not in ZDOS).
			// it reads FAT ID from disk EACH CALL (no disk change test).
			// the fn call 1C seems to return the various info in registers upon return,
			// and getfat() transfers it into the struct.
			struct fatinfo diskinfo = {0,0,0,0};
			getfat(2, &diskinfo);
			cout << "       FAT ID byte is: " << hex << (uint)diskinfo.fi_fatid << dec << endl;
			cout << "     bytes per sector: " << (uint)diskinfo.fi_bysec << endl;
			cout << "  sectors per cluster: " << (uint)diskinfo.fi_sclus << endl;
			cout << "   number of clusters: " << (uint)diskinfo.fi_nclus << endl;
			break;
		}
	}				// switch
}					// end while(1)
}						// end main

Old96TPI.txt - parsed boot loaders in various disk formats

11-15-99  Debug. This is everything readable from an H100 96tpi disk.

Boot Loader:
Starts with disk format table!  See zdos\DEFFMT.ASM
MS-defined entries:
              |start of table
              |Version of table
              |  |Sector size of disk(in bytes)
              |        |cluster factor
              |           |reserved sector count
              |                 |FAT count
              |                    |DIR entry count
              |                          |Physical sector count (5A0)
After here, Zenith-defined entries:
              |                                |Shift count (Log2 SECS)
      jmp +1f |                                   |Sectors/track
0000  EB 1F 00 02 00 02 04 01-00 02 90 00 A0 05 09 09   ................

      |Data area sector number
           |Shift count (Log2 cluster size)
              |Directory area sector number
                    |Flag byte (DSK_FLAG)
                       |Select byte (DSK_SEL)
                          |Port number of controller
                                |Date and time?
0010  0E 00 02 05 00 01 08 B0-00 17 15 12 03 2E 62 00   ..............b.
        |code starts
0020  00 0E 1F B8 00 14 8E C0-B9 00 3C BE 00 04 8B FE   ..........<.....
0030  FC F3 A4 EA 38 04 00 14-8C C8 8E D8 33 DB 8E C3   ....8.......3...
0040  26 8E 06 FE 03 26 8A 1E-09 00 89 1E 17 04 26 8A   &....&........&.
0050  1E 5A 00 08 1E 16 04 8E-C0 B0 08 E6 FC 8B 1E 13   .Z..............
0060  04 8A 0E 0E 04 D3 E3 F6-06 16 04 04 75 13 80 3E   ............u..>
0070  12 04 02 7D 0C E8 56 01-A8 08 74 05 80 0E 15 04   ...}..V...t.....
0080  0C B9 0B 00 8D B7 00 04-BF EB 04 F3 A6 75 3E 8B   .............u>.
0090  87 1A 04 2D 02 00 8A 0E-12 04 D3 E0 03 06 10 04   ...-............
00A0  A3 0F 05 8B 87 1C 04 8B-0E 04 04 49 03 C1 8A 0E   ...........I....
00B0  0E 04 D3 E8 A3 11 05 BB-0E 05 B0 02 E8 58 00 72   .............X.r
00C0  13 8C C8 05 40 00 8E D8-EA 00 00 40 00 BB F6 04   ....@......@....
00D0  B5 0D EB 05 BB 01 05 B5-0D 8A 07 53 51 9A 19 00   ...........SQ...
00E0  01 FE 59 5B 43 FE CD 75-F0 EB FE 49 4F 20 20 20   ..Y[C..u...IO   
00F0  20 20 20 53 59 53 0D 0A-4E 6F 20 53 79 73 74 65      SYS..No Syste
0100  6D 0D 0A 49 2F 4F 20 65-72 72 6F 72 0D 0A 00 00   m..I/O error....
0110  00 00 00 00 00 40 00 26-8B 47 01 A3 F8 05 A1 F8   .....@.&.G......
0120  05 F6 36 0F 04 FE C4 32-D2 F6 06 15 04 01 74 06   ..6....2......t.
0130  D0 E8 73 02 B2 02 88 16-F7 05 8A 2E 20 04 A2 20   ..s......... .. 
0140  04 E8 AA 00 B0 1F E8 77-00 F6 06 15 04 04 74 10   .......w......t.
0150  A0 20 04 E8 98 00 8A C5-E8 89 00 B0 1F E8 60 00   . ............`.
0160  E8 54 00 8A C4 E8 81 00-A0 16 04 0C 40 E8 6B 00   .T..........@.k.
0170  8B 0E 04 04 06 26 C4 7F-05 FC B0 88 0A 06 F7 05   .....&..........
0180  9C FA 8B 16 17 04 83 C2-03 52 8B 16 17 04 EE 5A   .........R.....Z
0190  EC AA E2 FC A0 16 04 E8-41 00 E8 26 00 9D 07 84   ........A..&....
01A0  C0 74 02 F9 C3 FF 06 F8-05 26 89 7F 05 26 FF 4F   .t.......&...&.O
01B0  03 74 03 E9 68 FF C3 51-B9 A0 0F 49 75 FD 59 C3   .t..h..Q...Iu.Y.
01C0  E8 1D 00 E8 08 00 A8 01-74 F9 E8 06 00 C3 BA 05   ........t.......
01D0  00 EB 02 33 D2 03 16 17-04 EC C3 BA 04 00 EB 11   ...3............
01E0  33 D2 EB 0D BA 01 00 EB-08 BA 02 00 EB 03 BA 03   3...............
01F0  00 03 16 17 04 EE C3 00-00 00 56 46 3A 07 73 08   ..........VF:.s.

FAT1
0200  F9 FF FF 03 40 00 05 60-00 07 80 00 09 F0 FF 0B   ....@..`........
0210  C0 00 0D E0 00 0F 00 01-11 20 01 FF 4F 01 15 60   ......... ..O..`

...all H100 disks (48 and 96) seem to have this 'N' section
04B0  E5 E5 E5 E5 E5 E5 E5 E5-E5 E5 E5 F7 4E 4E 4E 4E   ............NNNN
04C0  4E 4E 4E 4E 4E 4E 4E 4E-4E 4E 4E 4E 4E 4E 4E 4E   NNNNNNNNNNNNNNNN
05F0  E5 E5 E5 E5 E5 E5 E5 E5-E5 E5 E5 E5 E5 E5 E5 E5   ................

FAT2 same
0600  F9 FF FF 03 40 00 05 60-00 07 80 00 09 F0 FF 0B   ....@..`........
09F0  E5 E5 E5 E5 E5 E5 E5 E5-E5 E5 E5 E5 E5 E5 E5 E5   ................

DIR
0A00  49 4F 20 20 20 20 20 20-53 59 53 27 00 00 00 00   IO      SYS'....
0A10  00 00 00 00 00 00 3D 4E-34 0B 02 00 01 3A 00 00   ......=N4....:..
0A20  4D 53 44 4F 53 20 20 20-53 59 53 27 00 00 00 00   MSDOS   SYS'....
0A30  00 00 00 00 00 00 38 6B-2A 09 0A 00 AF 42 00 00   ......8k*....B..
11F0  E5 E5 E5 E5 E5 E5 E5 E5-E5 E5 E5 E5 E5 E5 E5 E5   ................
END OF 9 SECTORS
1200  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

CHKDSK on an H100 disk:
D:\TEMP>chkdsk b:
Errors found, F parameter not specified
Corrections will not be written to disk
        1,515 lost allocation units found in 1,302 chains.
      775,680 bytes disk space would be freed
    1,213,952 bytes total disk space
      438,272 bytes available on disk
          512 bytes in each allocation unit
        2,371 total allocation units on disk
          856 available allocation units on disk
I tried Scandisk /checkonly.  It reported 2nd FAT out of date, and disk read 
error at directory entry #144.  It said FAT #1 was ok, and found no problems 
with the directory structure.

//----------------------------------------------------------------------------
Boot loader from RAMDRIVE (D:):
Look for references to its format OUTSIDE the FAT ID byte:
                                      |Sector size of disk(in bytes)
                                            |cluster factor
                                               |reserved sector count
              |text...................|     |  |
0000  EB 1D 90 52 44 56 20 31-2E 32 30 00 02 02 01 00   ...RDV 1.20.....
     |FAT count
        |DIR entry count
              |Physical sector count (5A0)
                    |new entries? (not provided for in DOS1)
                       |
                             |Sectors/track
     |  |=16  |=4096|id|
0010  01 10 00 00 10 F8 06 00-01 00 01 00 00 00 08 EB   ................
0020  FE 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41   .AAAAAAAAAAAAAAA

This is the FAT1  (there IS no FAT2).  Note the extra FFs in header.
ramdrive gets the FAT of a hard disk.

0200  F8 FF FF FF FF FF 05 60-00 07 80 00 09 A0 00 0B   .......`........
0210  C0 00 0D E0 00 0F 00 01-11 20 01 13 40 01 15 60   ......... ..@..`

//----------------------------------------------------------------------------
Boot loader from 360k 5" disk:
                                      |Sector size of disk(in bytes)
                                            |cluster factor
                                               |reserved sector count
     |jmp +3C |text...................|     |  |
0000  EB 3C 90 4D 53 44 4F 53-35 2E 30 00 02 02 01 00   .<.MSDOS5.0.....
     |FAT count
        |DIR entry count
              |Physical sector count (5A0)
                    |new entries? (not provided for in DOS1)
                       |
                             |Sectors/track
     |  |=112 |=720 |id|
0010  02 70 00 D0 02 FD 02 00-09 00 02 00 00 00 00 00   .p..............

0020  00 00 00 00 00 00 29 E4-09 1F 2B 4E 4F 20 4E 41   ......)...+NO NA

0030  4D 45 20 20 20 20 46 41-54 31 32 20 20 20 FA 33   ME    FAT12   .3
0040  C0 8E D0 BC 00 7C 16 07-BB 78 00 36 C5 37 1E 56   .....|...x.6.7.V

FAT1 of newly formatted 5"
0200  FD FF FF 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
FAT2 at 600 = same.

//----------------------------------------------------------------------------
Boot loader from freshly formatted 1.2M 5" disk:

0000  EB 3C 90 4D 53 44 4F 53-35 2E 30 00 02 01 01 00   .<.MSDOS5.0.....

0010  02 E0 00 60 09 F9 07 00-0F 00 02 00 00 00 00 00   ...`............

0020  00 00 00 00 00 00 29 49-41 05 09 4E 4F 20 4E 41   ......)IA..NO NA

0030  4D 45 20 20 20 20 46 41-54 31 36 20 20 20 FA 33   ME    FAT16   .3

FAT:
0200  F9 FF FF 00 00 00 00 00-00 00 00 00 00 00 00 00   ................

//----------------------------------------------------------------------------
Boot loader from an H100 48tpi disk:
chkdsk doesn't report a Vol. Serial Number for this disk.
See DOS2 chkdsk: it reports a creation date, which may be encoded here
somewhere.

0000  EB 1F 00 02 00 02 02 01-00 02 70 00 D0 02 09 09   ..........p.....

0010  0C 00 01 05 00 01 18 B0-00 33 0C 04 29 21 54 00   .........3..)!T.

0020  00 0E 1F B8 00 14 8E C0-B9 00 3C BE 00 04 8B FE   ..........<.....

//----------------------------------------------------------------------------
Boot loader from Hard disk C:

0000  EB 3C 90 4D 53 44 4F 53-35 2E 30 00 02 40 01 00   .<.MSDOS5.0..@..

0010  02 00 02 00 00 F8 81 00-3F 00 40 00 3F 00 00 00   ........?.@.?...

0020  C1 3C 20 00 80 00 29 02-1E 02 16 4E 4F 20 4E 41   .< ...)....NO NA

0030  4D 45 20 20 20 20 46 41-54 31 36 20 20 20 FA 33   ME    FAT16   .3
//----------------------------------------------------------------------------

Screenshot of Drives.xls showing FAT characteristics of various disk formats

Screenshot of Drives.xls showing FAT table characteristics for several disk formats.

The web page at http://home.cfl.rr.com/eaa/FloppyDisks.htm has a table similar to this one with some additional information you might find useful.

 

 

Valid HTML 4.01 Transitional Valid CSS
View content labeling at ICRA.
Copyright ©2008 Steven Whitney. Last modified 02/27/2008.