【问题标题】:Using BIOS int 13h to access sectors in different heads使用 BIOS int 13h 访问不同磁头中的扇区
【发布时间】:2020-04-12 06:53:10
【问题描述】:

我有一个每个磁道有 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

这不起作用。

附:我认为每个磁道的扇区数是 63 的原因是因为简单地设置 cl = 64 也不起作用

【问题讨论】:

  • @MichaelPetch 在我的机器中,我执行 int 0x13/ah=8 并在 cx 中得到 0x4f24 的值
  • @MichaelPetch 这很清楚!我现在应该希望得到正确的结果

标签: assembly x86 x86-16 bootloader osdev


【解决方案1】:

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

【讨论】:

  • 我在设备 80h(硬盘 0)上使用了 int 13h /ah=8。我得到以下值: ax =0; bx =0; cx =003Fh ; dx = 0F01h
  • ch等于00是什么意思?
  • @SuraajKS :这意味着驱动器上的最高柱面编号为 0。您的磁盘映像是否小于 516096 字节?您正在使用的磁盘映像文件的大小是多少?本质上,这意味着您的磁盘有 1 个柱面。
  • @SuraajKS 这就是原因。最大柱面为 0 表示驱动器上总共有 0+1=1 个柱面。我知道磁盘映像必须小于 516096 字节,因为 head=16 和 SPT=63。 1柱*16头*63 SPT*512=516096字节。
  • @SuraajKS :为了最兼容,永远不要读取跨越磁道边界的多个扇区。一些 BIOS(最现代的)确实允许您越过磁道边界(AKA 多磁道读取),但您绝不能越过柱面边界。如果您一次读取 1 个扇区,那么您永远不必担心这些问题。
猜你喜欢
  • 2020-02-22
  • 1970-01-01
  • 2013-11-20
  • 1970-01-01
  • 2011-04-08
  • 2019-09-05
  • 2013-11-02
  • 2014-09-16
相关资源
最近更新 更多