TL;DR:驱动器(或模拟器或虚拟机中的驱动器映像)的大小可能会影响 BIOS 报告的磁头、柱面和每磁道 (SPT) 扇区数。您可能通过查询 BIOS 获得的值可能因大小而异,并且可能因计算机和 BIOS 使用的CHS translation scheme 而异。
如果您的 BIOS 报告驱动器具有 16 个磁头和 63 个 SPT,则 CHS(0, 1, 1) 应该是驱动器上的第 64 个扇区。然而,如果 BIOS 报告 16 个磁头和 36 个 SPT,则第 64 个扇区是 CHS(0,1,28)。如果使用带有 CHS 寻址的硬盘驱动器,请始终在引导加载程序运行时向 BIOS 查询磁头数和 SPT 并计算逻辑块地址 (LBA)。强烈建议不要将柱面数、磁头数和 SPT 值硬编码到引导加载程序中。
不要使用 0x80 作为驱动器编号,而是使用 BIOS 传递给引导加载程序的 DL 中的值。使用该值而不是硬编码引导驱动器号。我有一些 General Bootloader Tips 涵盖了您在进行传统引导加载程序开发时可能希望考虑的其他事项。
Cylinder, Head, Sector (CHS) 寻址源自在圆形盘片(或软盘)上使用磁性介质的介质。维基百科有一个很好的article 关于 CHS 的主题。从物理意义上描述 CHS 的图表:
Image source Wikipedia
磁道是一个同心圆(位于盘片的一侧),被划分为称为扇区的多个部分。驱动头读取盘片一侧的磁道。需要 2 个磁头才能访问物理盘片的两侧。圆柱体是媒体上所有轨道的集合,因为它们一个接一个地出现。
随着时间的推移,柱面、扇区和磁头的概念不再与物理实现相匹配,但 CHS 寻址仍用于访问设备上的数据以实现兼容性。 Logical Block Addressing (LBA) 最终淘汰了 CHS,并且是在 BIOS 中显示为硬盘驱动器的媒体上的首选方法。 CHS 寻址仍用于将自身作为软盘介质呈现给 BIOS 的设备上。并非所有 BIOS 都支持 LBA 访问软盘介质,在这种情况下,CHS 寻址仍然是首选机制。
您之前的问题和 cmets 打算在硬盘驱动器上使用 CHS 寻址。正如我之前提到的,硬盘驱动器最好使用Int 13h/AH=42H,以避免使用CHS,而是使用LBA。逻辑块地址从 0 开始,直到驱动器上的总扇区数减 1。处理 LBA 要容易得多。
使用依赖 CHS 的 BIOS 功能的缺点是扇区固定为 512 字节。使用LBA assist BIOS translation(由 BOCHS 和 QEMU 支持),您可以从驱动器读取的最大扇区数为 1024*255*63=16450560 个扇区或 16450560*512=8422686720 (~7.844GiB/~8.423GB)。使用 CHS 寻址,您将无法读取超出范围的内容。
您可以使用我之前在related answer 中描述的公式转换 LBA:
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
您可以使用以下方法进行反向计算(CHS 到 LBA):
CHS tuples can be mapped to LBA address with the following formula:
LBA = (C × HPC + H) × SPT + (S - 1)
为了计算磁盘上给定 LBA 的 CHS,您需要知道每磁道的扇区数 (SPT) 和磁头数。 Int 13h/AH=8h 可用于在运行时在引导加载程序中从 BIOS 检索这些值。在related answer 中,我提供了使用Int 13h/AH=8h 获取SPT 和Heads 并从32 位LBA 计算CHS 的示例代码,然后使用这些值读取带有Int 13h/AH=2h 的扇区
LBA 到 CHS 转换器
以下 C 程序采用 3 个参数。 HEADS、SPT 和 LBA 使用上述公式计算 CHS:
#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;
}
如果你想从 LBA 计算 CHS,这个程序会很方便。在您的情况下,您想知道磁盘上第 64 个扇区的 CHS 值。 LBA 是基于 0 的,因此 LBA 64-1=63。在您的 cmets/chat 中,BIOS 报告 SPT=36 和 Heads=16。如果你编译并运行上面的程序:
gcc lbatochs.c -o lbatochs
./lbatochs 16 36 63
结果应该是:
SPT = 36, Heads = 16
LBA = 63 is CHS = (0, 1, 28)
DH = 0x01, CH = 0x00, CL = 0x1C
对于 BIOS 报告的具有 SPT 63 和 16 磁头的驱动器,LBA 63 的结果应如下所示:
./lbatochs 16 63 63
SPT = 63, Heads = 16
LBA = 63 is CHS = (0, 1, 1)
DH = 0x01, CH = 0x00, CL = 0x01