#### 硬盘的读写 个人计算机中的 PATA/SATA 接口,每个 PATA 和 SATA 接口分配了 8个端口。但是,ICH 芯片内部通常集成了两个 PATA/SATA 接口,分别是主硬盘接口和副硬盘接口。这样一来,**主硬盘接口分配的端口号是 0x1f0~0x1f7,副硬盘接口分配的端口号是 0x170~0x177。** 硬盘读写的基本单位是扇区。就是说,至少读取或写入一个扇区,而不能仅读写一个扇区中的几个字节。 ​ 最早的逻辑扇区编址方法是LBA28,使用 28 个比特来表示逻辑扇区号,从逻辑扇区 0x0000000 到 0xFFFFFFF,共可以表示 2^28=268435456 个扇区。每个扇区有 512 字节,所以 LBA28 可以管理 128 GB 的硬盘。 下面采用LBA28访问硬盘。 --- **1.设置要读取的扇区数量**。这个数值要写入0x1f2端口。这是一个8位端口,因此每次只能读写255个扇区: ``` mov dx,0x1f2 mov al,0x01 ;1个扇区 out dx,al ``` ​ 注意:如果写入的值为0,则表明要读取256个扇区。每读取一个扇区,这个数值就减一。因此,如果在读写过程中发生错误,该端口包含着尚未读取的扇区数。 **2.设置LBA扇区号。** ​ 扇区的读写是连续的,因此只需要给出第一个扇区的编号就可以了。28 位的扇区号太长,需要将其分成 4 段,分别写入端口 0x1f3、0x1f4、0x1f5 和 0x1f6 号端口。其中,0x1f3 号端口存放的是 0~7 位;0x1f4 号端口存放的是 8~15 位;0x1f5 号端口存放的是 16~23 位,最后 4 位在 0x1f6 号端口。 例:假定需要读写的起始逻辑扇区号为0x02,代码如下 ``` mov dx,0x1f3 mov al,0x02 out dx,al ;LBA地址7~0 inc dx ;0x1f4 mov al,0x00 out dx,al ;LBA地址15~8 inc dx ;0x1f5 out dx,al ;LBA地址23~16 inc dx ;0x1f6 mov al,0xe0 ;LBA模式,主硬盘,以及LBA地址27~24 ``` 在现行的体系下,每个 PATA/SATA 接口允许挂接两块硬盘,分别是主盘(Master)和从盘(Slave)。**0x1f6 端口的低 4 位用于存放逻辑扇区号的 24~27位,第 4 位用于指示硬盘号,0 表示主盘,1 表示从盘。高 3 位是“111”,表示 LBA 模式。** | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | | L | B | A | 硬盘 | 逻辑 | 扇 | 区 | 号 | **3.向端口0x1f7写入0x20,请求硬盘读。**这也是一个8位端口: ``` mov dx,0x1f7 mov al,0x20 ;读命令 out dx,al ``` **4.等待读写操作完成。**端口0x1f7既是命令端口,又是状态端口。在其操作期间,0x1f7端口的第7位置为`1`一旦硬盘准备就绪,它就会将此位清零,同时将第三位设为`1`。准备发送或接受数据。实现代码如下: ``` mov dx,0x1f7 .waits: in al,dx and al,0x88 ;寄存器AL中除第七位和第三位,其他全部清0 cmp al,0x08 ;此时,如果寄存器AL中的二进制数是0x08,说明可以退出等待状态 jnz .waits ;不忙,且硬盘已准备好数据传送 ``` | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | | BSY | | | | DRQ | | | ERR | BSY位为1说明忙,DRQ位为1说明已准备好和主机交换数据,ERR为1表明前一个命令执行错误,具体原因可访问端口0x1f1 **5.连续取出数据。** ​ 0x1f0 是硬盘接口的数据端口,而且还是一个 16 位端口。一旦硬盘控制器空闲,且准备就绪,就可以连续从这个端口写入或者读取数据。下面的代码假定是从硬盘读一个扇区(512 字节,或者 256 字节),读取的数据存放到由段寄存器 DS 指定的数据段,偏移地址由寄存器 BX 指定: ``` mov cx,256 ;总共要读取的字数 mov dx,0x1f0 .readw: in ax,dx mov [bx],ax add bx,2 loop .readw ``` 最后,0x1f1 端口是错误寄存器,包含硬盘驱动器最后一次执行命令后的状态(错误原因)。