硬盘的读写

个人计算机中的 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 端口是错误寄存器,包含硬盘驱动器最后一次执行命令后的状态(错误原因)。