问题
I have a disk with 63 sectors per track. (I assume, based on my observations) I want to read sectors on a 16 bit bootloader using int 13h. For example, if I want to read sector number 63, I would do the following:
mov dl,0x80;Drive number
mov dh,0 ;This is head number/platter number
mov ch,0 ;This is cylinder number
mov cl,63 ;Sector number
mov ah,0x02 ;interrupt function
mov al,1 ;Number of sectors to be read
xor bx,bx
mov es,bx ;Making es=0
mov bx,0x8000 ;Any random buffer address
int 0x13
The above code works as expected.
Now I want to read sector 64. I believe that it would be cylinder 0, head 1, sector 1. I use:
mov dl,0x80;Drive number
mov dh,1 ;This is head number/platter number
mov ch,0 ;This is cylinder number
mov cl,1 ;Sector number
mov ah,0x02 ;interrupt function
mov al,1 ;Number of sectors to be read
xor bx,bx
mov es,bx ;Making es=0
mov bx,0x8000 ;Any random buffer address
int 0x13
This doesn't work.
P.S. The reason I believe that the number of sectors per track is 63 is because simply setting cl = 64 also does not work
回答1:
TL;DR: The size of a drive (or drive image in emulators or virtual machines) may impact the number of heads, cylinders and sectors per track (SPT) reported by the BIOS. The values you may get by querying the BIOS may differ depending on the size and may vary from computer to computer and the CHS translation scheme being used by the BIOS.
If you have a BIOS that reports a drive as having 16 heads and 63 SPT then a CHS(0, 1, 1) should be the 64th sector on the drive. If however the BIOS reports 16 heads and 36 SPT then the 64th sector is CHS(0, 1, 28). If using a hard drive with CHS addressing always query the BIOS for the number of heads and SPT and compute logical block addresses (LBA) when your bootloader runs. It is highly recommended to not hard code the number of cylinders, number of heads, and SPT value into a bootloader.
Rather than use 0x80 for a drive number, use the value in DL that the BIOS passes to your bootloader. Use that value rather than hard coding the boot drive number. I have some General Bootloader Tips that covers that and other things you may wish to consider in doing legacy bootloader development.
Cylinder, Head, Sector (CHS) addressing came about from media that used magnetic media on round platters (or floppy disk). Wikipedia has a good article on the subject of CHS. A diagram depicting CHS in a physical sense:
Image source Wikipedia
A track is a concentric circle (on a single side of a platter) that is divided into sections called a sector. A drive head reads the tracks on one side of a platter. 2 heads are required to access both sides of a physical platter. A cylinder is the collection of all the tracks on the media as they appear one on top of another.
After time the concept of cylinders, sectors, and heads no longer matched the physical implementation however CHS addressing was still used to access data on a device for compatibility. Logical Block Addressing (LBA) eventually obsoleted CHS and is the preferred method on media that appears as a hard drive to the BIOS. CHS addressing is still used on devices that present themselves as floppy disk media to the BIOS. LBA access to floppy media is not supported by all BIOSes, and CHS addressing is still the preferred mechanism in that case.
Your previous question and comments are intent on using CHS addressing on a hard drive. As I previously mentioned it is preferred with hard drives to use Int 13h/AH=42H to avoid using CHS at all and using LBA instead. A logical block address starts at 0 and goes up to the total sector count on the drive minus 1. Dealing with an LBA is much easier.
The disadvantages of using BIOS functions that rely on CHS are that sectors are fixed to 512 bytes. With LBA assist BIOS translation (Supported by BOCHS and QEMU) the maximum number of sectors you can read from a drive is 1024*255*63=16450560 sectors or 16450560*512=8422686720 (~7.844GiB/~8.423GB). You won't be able to read beyond that with CHS addressing.
You can convert an LBA using a formula I have previously described in a related answer:
LBA is the logical block address HPC is the maximum number of heads per cylinder (reported by disk drive, typically 16 for 28-bit LBA) SPT is the maximum number of sectors per track (reported by disk drive, typically 63 for 28-bit LBA) LBA addresses can be mapped to CHS tuples with the following formula ("mod" is the modulo operation, i.e. the remainder, and "÷" is integer division, i.e. the quotient of the division where any fractional part is discarded): C = (LBA ÷ SPT) ÷ HPC H = (LBA ÷ SPT) mod HPC S = (LBA mod SPT) + 1 where C, H and S are the cylinder number, the head number, and the sector number
You can do the calculation in the reverse direction (CHS to LBA) using:
CHS tuples can be mapped to LBA address with the following formula: LBA = (C × HPC + H) × SPT + (S - 1)
In order to compute CHS for a given LBA on the disk you need to know the Sectors Per Track (SPT) and the number of heads. Int 13h/AH=8h can be used to retrieve these values from the BIOS at runtime in the bootloader. In a related answer I provide example code that uses Int 13h/AH=8h to get SPT and Heads and computes CHS from a 32-bit LBA and then use those values to read a sector with Int 13h/AH=2h
LBA to CHS converter
The following C program takes 3 arguments. HEADS, SPT, and LBA to compute CHS using the formula above:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
unsigned int heads;
unsigned int spt; /* sectors per track */
unsigned int h; /* Head */
unsigned int c; /* Cylinder */
unsigned int s; /* Sector */
unsigned int lba;
if (argc != 4) {
fprintf (stderr, "Usage: HEADS SPT LBA\n");
return 1;
}
heads = atoi(argv[1]);
if (heads > 255 || heads < 1) {
fprintf (stderr, "Error: HEADS must be <= 255 and >= 1\n");
return 1;
}
spt = atoi(argv[2]);
if (spt > 63 || spt < 1) {
fprintf (stderr, "Error: SPT must be <= 63 and >= 1\n");
return 1;
}
lba = atoi(argv[3]);
/* Proper calculation */
c = (lba / spt) / heads;
h = (lba / spt) % heads;
s = (lba % spt) + 1;
printf ("SPT = %u, Heads = %u\n", spt, heads);
printf ("LBA = %u is CHS = (%u, %u, %u)\n\n", lba, c, h, s);
if (c >= 1024)
printf ("Can't use CHS because %u cylinders >= 1024, use LBA!\n", c);
else
printf ("DH = 0x%02X, CH = 0x%02X, CL = 0x%02X\n",
h, c & 0xff, s | ((c >> 2) & 0xc0));
return 0;
}
This program can be convenient if you want to compute CHS from an LBA. In your case you want to know the CHS values for the 64th sector on the disk. LBA's are 0 based so that is LBA 64-1=63. In your comments/chat the BIOS is reporting SPT=36 and Heads=16. If you compile and run the program above with:
gcc lbatochs.c -o lbatochs
./lbatochs 16 36 63
The result should be:
SPT = 36, Heads = 16 LBA = 63 is CHS = (0, 1, 28) DH = 0x01, CH = 0x00, CL = 0x1C
For a drive that the BIOS reports with SPT 63 and 16 heads the results should look like this for LBA 63:
./lbatochs 16 63 63
SPT = 63, Heads = 16 LBA = 63 is CHS = (0, 1, 1) DH = 0x01, CH = 0x00, CL = 0x01
来源:https://stackoverflow.com/questions/59413722/using-bios-int-13h-to-access-sectors-in-different-heads