使用BIOS int 13h访问不同磁头的扇区

Using BIOS int 13h to access sectors in different heads

我有一个每磁道有 63 个扇区的磁盘。 (我假设,根据我的观察) 我想使用 int 13h 读取 16 位引导加载程序上的扇区。 例如,如果我想读取扇区号 63,我将执行以下操作:

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

以上代码按预期工作。

现在我想读取 64 扇区。我认为它应该是 0 柱面,1 磁头,1 扇区。 我使用:

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

这行不通。

P.S。我认为每个轨道的扇区数是 63 的原因是因为简单地设置 cl = 64 也不起作用

TL;DR:驱动器(或模拟器或虚拟机中的驱动器映像)的大小可能会影响报告的每个磁道 (SPT) 的磁头数、柱面数和扇区数通过BIOS。通过查询 BIOS 获得的值可能因大小而异,并且可能因计算机和 BIOS 使用的 CHS translation scheme 而异。

如果您的 BIOS 报告驱动器具有 16 个磁头和 63 个 SPT,则 CHS(0, 1, 1) 应该是驱动器上的第 64 个扇区。但是,如果 BIOS 报告 16 个磁头和 36 个 SPT,则第 64 个扇区为 CHS(0, 1, 28)。如果使用带有 CHS 寻址的硬盘驱动器,请始终在引导加载程序 运行s 时查询 BIOS 的磁头数和 SPT 并计算逻辑块地址 (LBA)。强烈建议不要将柱面数、磁头数和 SPT 值硬编码到引导加载程序中。

不要使用 0x80 作为驱动器编号,而是使用 BIOS 传递给引导加载程序的 DL 中的值。使用该值而不是对引导驱动器编号进行硬编码。我有一些 涵盖了您在进行遗留引导加载程序开发时可能希望考虑的其他事项。


柱面、磁头、扇区 (CHS) 寻址来自于在圆形盘片(或软盘)上使用磁性介质的介质。维基百科关于 CHS 的主题有很好的 article。从物理意义上描述 CHS 的图表:

Image source Wikipedia


轨道是一个同心圆(在盘片的单侧),被分成称为扇区的部分。驱动器磁头读取盘片一侧的轨道。需要 2 个磁头才能访问物理盘片的两侧。柱面是媒体上所有轨道的集合,因为它们一个接一个地出现。

一段时间后,柱面、扇区和磁头的概念不再与物理实现相匹配,但 CHS 寻址仍用于访问设备上的数据以实现兼容性。 Logical Block Addressing (LBA) 最终淘汰了 CHS,并且是在 BIOS 中显示为硬盘驱动器的媒体上的首选方法。 CHS 寻址仍用于将自身呈现为 BIOS 软盘介质的设备。并非所有 BIOS 都支持对软盘媒体的 LBA 访问,在这种情况下,CHS 寻址仍然是首选机制。


您之前的问题和评论是关于在硬盘驱动器上使用 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 寻址读取更多内容。

您可以使用我之前在 :

中描述的公式转换 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 can be used to retrieve these values from the BIOS at runtime in the bootloader. In a 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 到 CHS 转换器

下面的 C 程序有 3 个参数。使用以上公式计算 CHS 的 HEADS、SPT 和 LBA:

#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。在您的 comments/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