用I/O端口直接控制硬盘 - 图1

好的,以下是通过直接访问硬盘 I/O 端口进行硬盘控制的详细完整内容,包括 I/O 端口结构表、寄存器的作用及使用方法,以及具体的操作步骤。这将帮助您理解如何在不使用 BIOS 中断的情况下进行硬盘的低级读写操作。


硬盘 I/O 端口结构表(主 IDE 通道)

硬盘控制器在主 IDE 通道上拥有一组固定的 I/O 端口,用于实现硬盘的底层操作。以下是这些端口的详细结构。

端口地址 寄存器名称 位宽 读/写 作用描述
0x1F0 数据寄存器 16 位 读/写 用于数据传输,每次传输 16 位(2 字节)。
0x1F1 错误/特征寄存器 8 位 读/写 读模式下为错误寄存器,写模式下为特征寄存器,配置硬盘操作特性。
0x1F2 扇区计数寄存器 8 位 读/写 指定要读/写的扇区数量。0 表示 256 个扇区。
0x1F3 LBA 地址低 8 位 8 位 读/写 指定 LBA 地址的低 8 位。
0x1F4 LBA 地址中间 8 位 8 位 读/写 指定 LBA 地址的中间 8 位。
0x1F5 LBA 地址高 8 位 8 位 读/写 指定 LBA 地址的高 8 位。
0x1F6 驱动器/头寄存器 8 位 读/写 用于选择驱动器和磁头,以及 LBA 地址的 24-27 位。
0x1F7 命令/状态寄存器 8 位 读/写 读模式下为状态寄存器,写模式下为命令寄存器。

辅助控制端口

端口地址 寄存器名称 位宽 读/写 作用描述
0x3F6 驱动器控制寄存器 8 位 控制硬盘的复位、禁用中断等操作。
0x3F6 驱动器地址寄存器 8 位 读取当前驱动器状态。

各寄存器的功能说明

  1. 数据寄存器(0x1F0

    • 用于硬盘和 CPU 之间的数据传输,读操作时从此端口读取数据,写操作时将数据写入此端口。
  2. 错误/特征寄存器(0x1F1

    • 读模式:显示上次命令的错误信息,包括坏块(BB)、命令被中止(ABRT)等。
    • 写模式:配置硬盘特性,指定一些操作参数。
  3. 扇区计数寄存器(0x1F2

    • 用于指定本次读/写操作的扇区数量。写入 0 表示操作 256 个扇区。
  4. LBA 地址寄存器(0x1F3-0x1F5

    • 用于指定要访问的 LBA 地址,分别为低 8 位、中 8 位和高 8 位。
  5. 驱动器/头寄存器(0x1F6

    • 位 6:设置为 1 表示使用 LBA 模式。
    • 位 5:选择驱动器,0 表示主盘,1 表示从盘。
    • 位 0-3:存放 LBA 地址的 24-27 位。 用I/O端口直接控制硬盘 - 图2
  6. 命令/状态寄存器(0x1F7

    • 读模式:提供硬盘状态信息,包括忙碌(BSY)、数据请求(DRQ)等状态。
    • 写模式:用于发送命令给硬盘,例如 0x20 表示读命令,0x30 表示写命令。 用I/O端口直接控制硬盘 - 图3
  7. 辅助控制端口(0x3F6

    • 控制硬盘的复位、中断禁用等操作。

硬盘读写操作步骤

通过 I/O 端口控制硬盘的操作主要包括读和写两个步骤,以下是这两个操作的详细流程:

读取扇区操作

  1. 等待硬盘空闲:读取 0x1F7 状态寄存器的 BSY 位,等待该位清零,表示硬盘空闲。
  2. 指定扇区数量:将要读取的扇区数写入 0x1F2 扇区计数寄存器。
  3. 设置 LBA 地址:依次向 0x1F30x1F40x1F5 写入 LBA 地址的低、中、高字节,将 LBA 的高 4 位写入 0x1F6 驱动器/头寄存器。
  4. 选择驱动器并发出读命令:在 0x1F7 中写入 0x20(读命令)。
  5. 等待数据准备:读取 0x1F7 中的 DRQ 位,等待该位被置位,表示数据准备就绪。
  6. 读取数据:从 0x1F0 数据寄存器读取数据,每次读取 2 字节(16 位),直到读取完指定的扇区数。

示例代码:读取一个扇区

以下是汇编代码示例,从硬盘读取一个扇区数据到指定内存位置:

  1. mov dx, 0x1F6 ; 设置驱动器和头
  2. mov al, 0xE0 ; LBA 模式,选择主盘
  3. out dx, al
  4. mov dx, 0x1F2 ; 设置扇区计数
  5. mov al, 1 ; 读取一个扇区
  6. out dx, al
  7. mov dx, 0x1F3 ; 设置 LBA 低字节
  8. mov al, LBA_low ; LBA 地址的低 8
  9. out dx, al
  10. mov dx, 0x1F4 ; 设置 LBA 中字节
  11. mov al, LBA_mid ; LBA 地址的中 8
  12. out dx, al
  13. mov dx, 0x1F5 ; 设置 LBA 高字节
  14. mov al, LBA_high ; LBA 地址的高 8
  15. out dx, al
  16. mov dx, 0x1F7 ; 写入读命令
  17. mov al, 0x20 ; 读命令
  18. out dx, al
  19. .wait_drq:
  20. in al, dx ; 检查 DRQ
  21. test al, 0x08 ; 检查 DRQ
  22. jz .wait_drq ; 如果 DRQ 未置位,等待
  23. mov dx, 0x1F0 ; 读取数据
  24. in ax, dx ; 从数据端口读取 16

写入扇区操作

  1. 等待硬盘空闲:读取 0x1F7,检查 BSY 位是否清零。
  2. 指定扇区数量:将要写入的扇区数写入 0x1F2
  3. 设置 LBA 地址:依次向 0x1F30x1F40x1F5 写入 LBA 地址,将 LBA 的高 4 位写入 0x1F6
  4. 选择驱动器并发出写命令:向 0x1F7 写入 0x30(写命令)。
  5. 等待数据请求:读取 0x1F7 中的 DRQ 位,等待数据请求信号。
  6. 写入数据:向 0x1F0 数据寄存器写入数据,每次写入 2 字节,直到写入完指定的扇区数。

总结

通过直接访问硬盘 I/O 端口,可以在不调用 BIOS 中断的情况下

直接进行硬盘控制。主要的寄存器包括数据传输、错误检测、扇区计数和地址控制等。读写操作依赖于状态寄存器的 BSYDRQ 位来同步硬盘的状态。掌握这些步骤后,可以在操作系统开发或嵌入式系统中灵活地实现硬盘的底层读写操作。