I/O端口和端口访问
本质上,端口就是一些寄存器,类似于处理器内部的寄存器。不同之处仅仅在于,这些叫作端口的寄存器位于I/O接口电路中。
端口的不同的实现方式:
- 端口号是映射到内存地址空间的。比如,0x00000~0xE0000是真实的物理内存地址,而0xE0001~0xFFFFF是从很多I/O接口那里映射过来的,当访问这部分地址时,实际上是在访问I/O接口。
- 端口是独立编址的,不和内存发生关系。在这种计算机中,处理器的地址线既连接内存,也连接每个I/O接口。但是,处理器还有一个特殊的引脚M/IO#,在这里,“#”表示低电平有效。也就是说,当处理器访问内存时,它会让M/IO#引脚呈高电平,这里,和内存相关的电路就会打开;相反,如果处理器访问I/O端口,那么M/IO#引脚呈低电平,内存电路被禁止。
PATA/SATA的几个端口
- 命令端口(当向该端口写入0x20时,表明是从硬盘读数据;写入0x30时,表明是向硬盘写数据)
- 状态端口(处理器根据这个端口的数据来判断硬盘工作是否正常,操作是否成功,发生了哪种错误)
- 参数端口(处理器通过这些端口告诉硬盘读写的扇区数量,以及起始的逻辑扇区号)
- 数据端口(通过这个端口连续地取得要读出的数据,或者通过这个端口连续地发送要写入硬盘的数据)
端口指令:
in命令,读端口数据:
- in指令的目的操作数必须是寄存器AL或者AX,当访问8位的端口时,使用寄存器AL;访问16位的端口时,使用AX。in指令的源操作数应当是寄存器DX,用来指定端口号。
- 例如:
in al, dx
,in ax, dx
,in ax, 0x01
。
out命令,向端口写:
- 目的操作数可以是8位立即数或者寄存器DX,源操作数必须是寄存器AL或者AX。
- 例如:
out dx, ax
,out dx, al
,out 0x01, ax
- in,out指令不影响任何标志位。
硬盘读取(独立编址)
- 逻辑扇区编址方法LBA28:使用28比特来表示逻辑扇区号,从逻辑扇区0x0000000到0xFFFFFFF,共可以表示2^28=268435456个扇区。每个扇区有512字节,所以LBA28可以管理128 GB的硬盘。
主硬盘控制器被分配了8位端口,端口号从0x1f0到0x1f7。
- 设置要读取的扇区数量。这个数值写入0x1f2端口。这是个8位端口。如果写入的值为0,则表示要读取256个扇区。每读一个扇区,这个数值就减1。因此,如果在读写过程中发生错误,该端口包含着尚未读取的扇区数。
设置起始LBA扇区号。扇区的读写是连续的,因此只需要给出第一个扇区的编号。28位的扇区号太长,需要将其分成4段,分别写入端口0x1f3、0x1f4、0x1f5和0x1f6。
- 0x1f3号端口存放的是0~7位;
- 0x1f4号端口存放的是8~15位;
- 0x1f5号端口存放的是16~23位,
- 0x1f6号端口存放的是24~27位,第4位用于指示硬盘号,0表示主盘,1表示从盘。高3位是“111”,表示LBA模式。
- 端口0x1f7既是命令端口,又是状态端口。在通过这个端口发送读写命令之后,硬盘开始工作。在它内部操作期间,它将0x1f7端口的第7位置“1”,表明自己很忙。一旦硬盘系统准备就绪,它再将此位清零,同时将第3位置“1”。
- 0x1f1端口是错误寄存器,包含硬盘驱动器最后一次执行命令后的状态(错误原因)。
- 0x1f0是硬盘接口的数据端口,16位端口。硬盘准备就绪后,在此端口读取数据。
main:
mov ax, 0x7c0
mov ds, ax
mov ax, 0xf00
mov es, ax
mov cx, 10
xor bx, bx
mov di, 0
; call read_disk_one_sector
read_disk_one_sector: ; LBA逻辑扇区号高位存储在bx, 低位存储在cx中, 读取的数据存储在es:di
; 向磁盘接口写入读写信息
mov al, 1
mov dx, 0x1f2
out dx, al; 要读取多少个扇区
mov al, cl
mov dx, 0x1f3
out dx, al ; 0x1f3号端口存放的是0~7位
mov al, ch
mov dx, 0x1f4
out dx, al ; 0x1f4号端口存放的是8~15位
mov al, bl
mov dx, 0x1f5
out dx, al ; 0x1f5号端口存放的是16~23位
mov al, bh
mov dx, 0x1f6 ; 0x1f6端口的低4位用于存放逻辑扇区号的24~27位,第4位用于指示硬盘号,0表示主盘,1表示从盘。高3位是“111”,表示LBA模式。
and al, 0b00001111 ; 低四位不变
or al, 0xe0 ; 高四位置1110
out dx, al; 0x1f6号端口存放的是24~27位
; 写入控制信息, 读
mov dx, 0x1f7
mov al, 0x20
out dx, al
waits:
in al, dx
and al, 0x88
cmp al, 0x08
jne waits
mov cx, 256
mov dx, 0x1f0 ; 0x1f0是硬盘接口的数据端口,且16位端口。
read:
in ax, dx
mov [es:di], ax
add di, 2
loop read
jmp near $
; 0x1f1端口是错误寄存器
; ret
end_file:
times 510-($-$$) db 0
db 0x55
db 0xaa
Comments | NOTHING