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

Sur*_*K S 2 x86 assembly osdev bootloader x86-16

我有一个每磁道有 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
Run Code Online (Sandbox Code Playgroud)

上面的代码按预期工作。

现在我想读取扇区 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
Run Code Online (Sandbox Code Playgroud)

这不起作用。

PS 我之所以认为每条磁道的扇区数是 63 是因为简单地设置 cl = 64 也不起作用

Mic*_*tch 5

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

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

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


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

在此处输入图片说明

图片来源维基百科


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

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


您之前的问题和评论旨在在硬盘驱动器上使用 CHS 寻址。正如我之前提到的,硬盘驱动器最好使用Int 13h/AH=42H以避免完全使用 CHS 而使用 LBA。逻辑块地址从 0 开始,直到驱动器上的总扇区数减 1。处理 LBA 容易得多。

使用依赖 CHS 的 BIOS 功能的缺点是扇区固定为 512 字节。使用LBA 辅助 BIOS 转换(由 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
Run Code Online (Sandbox Code Playgroud)

您可以使用以下方法进行反向计算(CHS 到 LBA):

CHS tuples can be mapped to LBA address with the following formula:

LBA = (C × HPC + H) × SPT + (S - 1)
Run Code Online (Sandbox Code Playgroud)

为了计算磁盘上给定 LBA 的 CHS,您需要知道每磁道扇区数 (SPT) 和磁头数。Int 13h/AH=8h可用于在运行时在引导加载程序中从 BIOS 检索这些值。在相关答案中,我提供了示例代码,该代码使用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;
}
Run Code Online (Sandbox Code Playgroud)

如果您想从 LBA 计算 CHS,这个程序会很方便。在您的情况下,您想知道磁盘上第 64 个扇区的 CHS 值。LBA 是基于 0 的,因此 LBA 64-1=63。在您的评论/聊天中,BIOS 报告 SPT=36 和 Heads=16。如果你编译并运行上面的程序:

gcc lbatochs.c -o lbatochs
./lbatochs 16 36 63
Run Code Online (Sandbox Code Playgroud)

结果应该是:

SPT = 36, Heads = 16
LBA = 63 is CHS = (0, 1, 28)

DH = 0x01, CH = 0x00, CL = 0x1C
Run Code Online (Sandbox Code Playgroud)

对于 BIOS 报告带有 SPT 63 和 16 磁头的驱动器,LBA 63 的结果应如下所示:

./lbatochs 16 63 63
Run Code Online (Sandbox Code Playgroud)
SPT = 63, Heads = 16
LBA = 63 is CHS = (0, 1, 1)

DH = 0x01, CH = 0x00, CL = 0x01
Run Code Online (Sandbox Code Playgroud)

  • @SuraajKS:为了实现最佳兼容性,切勿读取跨越磁道边界的多个扇区。某些 BIOS(大多数现代 BIOS)确实允许您跨越磁道边界(也称为多磁道读取),但绝不能跨越柱面边界。如果您一次读取 1 个扇区,那么您永远不必担心这些问题。 (2认同)