使用DMA方式,硬盘读写由DMA控制,数据在内存和I/O端口之间直接传输,而不需要CPU中转,传输速度比PIO方式快,降低了CPU的开销。
10.1 获取PCI-IDE配置
早期的IDE控制器挂接在ISA总线上,而目前主板上的芯片组一般都集成了一个PCI-IDE控制器,支持两个IDE通道。PCI-IDE控制器支持DMA功能,能够在IDE通道和内存之间建立直接的数据传输,而不必通过CPU。也就是说,PCI-IDE控制器可以作为总线主控设备,从IDE接口中读出数据写入到内存中(读扇区),或者从内存中读出数据写入到IDE接口中(写扇区)。
1. PCI-IDE控制器
如图10-1,PCI-IDE控制器就像一座桥梁一样,一端连接PCI总线,另一端连接IDE设备,能够在控制器中的DMA的指挥下,通过PCI总线在内存和IDE设备之间传输数据。
CPU |
PCI总线 |
内存 |
北桥、南桥 |
其它PCI设备 |
ATA设备 |
PCI-IDE控制器 |
IDE接口 |
2. PCI设备的配置空间
PCI具有即插即用的功能,支持自动的设备检测和配置。在系统启动时,操作系统扫描系统的各条PCI总线,枚举出总线上存在的PCI设备。操作系统读取PCI设备配置空间的寄存器,确定设备所需的地址空间、分配中断以及主设备对总线的访问要求等。
(1)总线、设备、功能
系统可以最多连接256条PCI 总线,每条PCI总线可以连接32个物理PCI设备。每个PCI设备可以包含一个到八个独立的PCI功能(即逻辑设备)。
(2)配置空间
对每一个功能,PCI设备都给它提供一个256字节的配置空间,由64个32位的配置寄存器组成。
图10-2显示了PCI功能配置空间中前面16个双字寄存器。这个区域的格式和用法由PCI规范定义,PCI设备必须按照PCI规范设置配置头区域有关字段。系统启动时,系统软件的配置程序读取配置头中的设备信息并根据设备的要求按照PCI规范配置设备。
31 | 16 | 15 | 0 |
|
设备ID | 供应商ID | 00H | ||
状态寄存器 | 命令寄存器 | 04H | ||
类代码 | 版本 | 08H | ||
内建自测 | 配置头类型 | 延迟时间 | Cache行大小 | 0CH |
基地址寄存器0 | 10H | |||
基地址寄存器1 | 14H | |||
基地址寄存器2 | 18H | |||
基地址寄存器3 | 1CH | |||
基地址寄存器4 | 20H | |||
基地址寄存器5 | 24H | |||
卡总线指针 | 28H | |||
子系统版本ID | 子系统供应商ID | 2CH | ||
扩展ROM基地址寄存器 | 30H | |||
保留 | 性能指针 | 34H | ||
保留 | 38H | |||
优先级请求 | 时间片请求 | 中断引脚 | 中断线 | 3CH |
图10-2 PCI配置空间头部的16个双字寄存器
供应商ID、设备ID、版本、类代码、子系统供应商ID、子系统版本ID,这6个寄存器用于识别设备类型,操作系统根据它们的内容,确定为设备装载哪个驱动程序。
•供应商ID:设备制造商的代码,由PCI SIG组织来分配。例如,值8086h代表Intel公司。
•设备ID:16位,由设备制造商分配,表示设备类型。例如,2416h代表Intel 82801AA (ICHAA) AC'97 Modem Controller。
•版本:8位,由设备制造商分配,表示设备的版本号。
•类代码:24位内容包含:基类型、子类型和可编程接口,每一项占1个字节。基类型代表设备的基本功能,设备子类型表示了该基类型中的设备的详细分类,可编程接口则表示该设备的寄存器编程接口。
·命令寄存器:提供了对设备响应和执行PCI访问能力的基本控制。
·状态寄存器:把功能的状态记录在PCI设备中。
·配置头类型:第6~第0位定义了配置空间头部的格式(00H=普通PCI设备,01H=PCI桥,02H=CardBus桥)。第7位定义了设备是单功能设备(=0)还是多功能设备(=1)。
·内建自测:BIST(Built-In Self-Test)寄存器,可以由主设备和/或目标设备提供,设置后,设备可以实现内置自检。延迟定时器也叫时间片寄存器,它对于执行猝发交易的主设备是强制性(可读/可写)。
·延迟时间:定义了以PCI时钟周期为单位的最小时间量,在这个时间片中,总线主设备只要起动一次新交易,就能保持总线所有权。起动交易后,总线主设备在每个时钟上升沿将延迟定时器减1。
·Cache行大小:用于存储器写和使失效命令,指出系统以双字为单位的Cache行大小(例如,一该寄存器的值为08h,表示Cache行容量为8个双字即32字节)。
·优先级请求:表示主设备访问总线的频度(多少时间访问总线一次,从仲裁器收到GNT #计算,250ns递增),决定总线仲裁器分配给主设备(假设仲裁器可编程)的优先级(以及仲裁器使用的仲裁方案)。
·时间片请求:由总线主设备提供,表示主设备要达到好的性能而希望保持PCI总线所有权的时间,指出设备进行一个猝发周期需要多长时间(以250ns为单位)。
·中脚引脚:指出功能连接了哪一个中断请求引脚。值01h到04h对应于PCI中断请求引脚INTA #至INTD #。
·中脚线:用于识别功能的PCI中断请求引脚(由中断引脚寄存器指定)连接到中断控制器的哪个输入端。
·基地址寄存器:为设备内的存储器和IO空间提供基地址的寄存器。第0位定义了该寄存器描述的是存储器(=0)还是I/O地址(=1)。存储器的基地址可以是64位的(第2-1位=10b),这时,使用2个基地址寄存器表示64位基地址。
·扩展ROM基地址寄存器:指出PCI设备内的扩展ROM起始存储器地址和长度。
3. 枚举PCI设备
微机系统提供了两个32位的I/O端口寄存器来访问PCI设备的配置空间。
·配置地址端口:I/O地址为0CF8H。将要访问的总线号、设备号、功能号和寄存器号写入到这个端口,前3项确定要读写哪一个PCI设备的配置空间,最后1项确定了要读写该配置空间的哪一个寄存器。如图10-3所示。
在配置地址端口中,总线号占8位,范围是0~255;设备号占5位,范围是0~31;功能号占3位,范围是0~7。
寄存器号共6位,其范围是0~63,指定256字节的配置空间中的某一个32位寄存器。例如,要读出配置头中的子系统版本ID,在图10-2中其偏移为2CH,寄存器号为2CH/4=9。
31 | 30 |
|
|
|
|
| 24 | 23 |
|
|
|
|
|
| 16 | 15 |
|
|
| 11 | 10 |
| 8 | 7 |
|
|
|
| 2 | 1 | 0 |
1 | 保留 | 总线号 | 设备号 | 功能号 | 寄存器号 | 0 | 0 |
图10-3 配置地址端口寄存器的格式
·配置数据端口:I/O地址为0CFCH。对配置空间的读、写都要通过这个端口进行,但程序首先要写入配置地址端口来指定要对哪一个PCI设备的寄存器。
按图10-3构造一个双字,写入到0CF8H端口后,再从0CFCH读入一个32位的值。在前面的例子中,从这个32位的值中取出其第31~16位,就是子系统版本ID。
枚举PCI设备的步骤为:
(1) 列出所有的总线号和设备号的组合,对每一个组合,设总线号为bus,设备号为dev,执行以下(2)至(5)步;
(2) 对功能号func,从0到7循环执行以下(3)至(5)步;
(3) index从0到63循环,读取<bus, dec, func>的全部64个寄存器。
(4) 如果func等于0,检查<bus, dec, func>的“配置头类型”寄存器的第7位。如果等于0,则该设备是单功能设备,退出第(2)步开始的循环;等于1时,该设备是多功能设备。
下列程序在DOS下运行,在枚举过程发现基类型=01H及子类型=01h的设备时,枚举结束,并显示出256个字节的配置空间。
;程序清单: pciide.asm(获取PCI-IDE配置空间)
.386P
DSEG SEGMENT USE16 ;16位数据段
CfgSpace DB 256 DUP(0) ;PCI设备的256字节配置空间
bus DW 0 ;bus号,0~255
dev DW 0 ;dev号,0~31
func DW 0 ;func号,0~7
index DW 0 ;index,0~63
DSEG ENDS ;数据段结束
SSEG SEGMENT PARA STACK ;堆栈段
DB 512 DUP (0)
SSEG ENDS ;堆栈段结束
;字符显示宏指令的定义
EchoCh MACRO ascii
mov ah,2
mov dl,ascii
int 21h
ENDM
CSEG SEGMENT USE16 ;1代码段
ASSUME CS:CSEG,DS:DSEG
; 搜索PCI-IDE设备, 获取PCI配置空间
FindPCIIDE PROC
; bus号从0循环到255
mov bus, 0
loop_bus:
; dev号从0循环到31
mov dev, 0
loop_dev:
; func号从0循环到7
mov func, 0
loop_func:
; index号从0循环到63
mov index, 0
loop_index:
;构造eax为一个32位双字, 写入0cf8h端口
;(1 << 31)|(bus << 16)|(dev << 11)|(func << 8)|(index << 2)
movzx eax,bus ;eax=bus
movzx ebx,dev ;ebx=dev
movzx ecx,func ;ecx=func
movzx edx,index ;dex=index
shl eax,16 ;eax=(bus<<16)
shl ebx,11 ;ebx=(dev<<11)
shl ecx,8 ;ecx=(func<<8)
shl edx,2 ;edx=(index<<2)
or eax,80000000h ;eax=(1<<31)||(bus<<16)
or eax,ebx ;eax=..||(dev << 11)
or eax,ecx ;eax=..||(func << 8)
or eax,edx ;eax=..||(index << 2)
;从0cf8h端口读取的配置寄存器将保存在CfgSpace[index*4]中
lea edi,CfgSpace[edx]
mov dx,0cf8h
out dx,eax ;eax写入到0cf8h端口
mov dx,0cfch
in eax,dx ;从0cfch端口读入
cld
stosd ;配置寄存器保存在CfgSpace中
inc index
cmp index, 64
jb loop_index ;index=0~63
cmp WORD PTR CfgSpace[0ah],0101h ;检查类代码寄存器
jz FindValidOne ;BaseClass=01h,Sub-Class=01h
cmp func,0 ;func=0时,检查为多功能设备
jnz NotFunc0 ;func=1时,不检查
test CfgSpace[0eh],80h ;Bit7=1,<bus,dev>是多功能设备
jz NotMultiFunc ;Bit7=0,不是
NotFunc0:
inc func
cmp func, 8
jb loop_func ;index=0~7
NotMultiFunc:
inc dev
cmp dev, 32
jb loop_dev ;dev=0~31
inc bus
cmp bus, 256
jb loop_bus ;bus=0~255
FindValidOne:
ret
FindPCIIDE ENDP
Start PROC
mov ax,DSEG
mov ds,ax ;ds指向数据段
mov es,ax ;es指向数据段
call FindPCIIDE ;搜索PCI-IDE设备
lea si,CfgSpace ;显示配置空间中的256字节数据
cld
mov bp,256/16
NextLine: mov cx,16
NextCh: lodsb
push ax
shr al,4
call ToASCII
EchoCh al
pop ax
call ToASCII
EchoCh al
EchoCh ' '
loop NextCh
EchoCh 0dh
EchoCh 0ah
dec bp
jnz NextLine
mov ax,4c00h
int 21h
Start ENDP
ToASCII PROC
and al,0fh
cmp al,10
jae Over10
add al,'0'
ret
Over10:
add al,'A'-10
ret
ToASCII ENDP
CSEG ENDS ;代码段结束
END Start
在VirtualBox的DOS环境中,运行结果显示为:
86 80 10 70 07 00 00 00 00 8A 01 01 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01 C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 80 00 80 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
PCI-IDE设备的配置空间遵循图10-2的格式。PCI规范中规定,基类型(Base Class)=01h,表示大容量存储控制器(mass storage controller)。在这个基类型之下,又分为若干子类型(Sub-Class)。子类型=00h,表示SCSI总线控制器;子类型=01h,表示IDE控制器;子类型=02h,表示软盘控制器等等。在基类型=01H,子类型=01h时,可编程接口(Interface)的最高位(Bit 7)表示该设备是否具有DMA主控功能。
在以上数据中,类代码为“8A 01 01”,其中基类型=01h,子类型=01h,可编程接口=8Ah。
10.2 硬盘DMA传输实验
PCI总线上的设备既可以作为PCI总线目标设备(Slave),也可以作为PCI总线的主控设备(Master)。Slave设备只能响应来自于CPU或其他设备的读写操作,不能主动地向总线发出读写操作;而Master设备除了能够响应读写操作外,还能够读写其他PCI设备、内存等。
PCI-IDE控制器中含有一个DMA控制器,它能够在硬盘和内存之间直接传送数据。在数据传送期间,这个DMA控制器接管PCI总线,产生对硬盘的I/O操作和对内存的读写操作,根据设定的传送字节数,在全部数据传送完成后,结束DMA传输。
1. PCI-IDE控制器中的DMA寄存器
PCI-IDE控制器作为一个PCI设备,在256字节的配置空间中,偏移020h处的基地址寄存器5作为DMA主控寄存器的首地址。在上面实验中,基地址寄存器5的内容为“01 C0 00 00”,即0000C001H。最后1位等于1,表示这个基地址属于I/O空间。DMA主控寄存器的首地址为0000C000H。
每个通道有3个寄存器,主控命令寄存器、主控状态寄存器和描述符指针寄存器。
(1)主控命令寄存器
主控命令寄存器的格式如图10‑4所示。第3位置为0时,表示读扇区,DMA传送方向为从IDE设备到内存;为1时,表示写扇区,方向为从内存到IDE设备。第0位置为0时,表示停止DMA传输;为1时表示启动DMA传输。
6 | 5 | 4 | 3 | 2 | 1 | 0 | |
保留 | 保留 | 保留 | 保留 | R/W | 保留 | 保留 | Start/Stop |
图 10‑4 主控命令寄存器
(2)主控状态寄存器
主控状态寄存器的格式如图10‑5所示。第0位置为1时,正在进行DMA传输;第1位置为1时,表示DMA传送出现了一个错误;第2位置为1时,IDE设备已产生一个中断请求(DMA传输已完成);第5位置为1时,表示设备0(主盘)能够执行DMA操作;第6位置为1时,表示设备1(从盘)能够执行DMA操作;第7位置为1时,表示设备0和设备1不能同时执行DMA操作。
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Simplex | D1DC | D0DC | 保留 | Interrupt | Error | Active |
图10‑5 主控状态寄存器
(3)物理区域描述符表指针寄存器
物理区域描述符表指针寄存器(Physical Region Descriptor Table Register,PRDTR)的格式如图10‑6所示。它是一个指向描述符表的指针,指针的第1位和第0位必须为0。描述符表中包含一个或多个物理区域描述符(Physical Region Descriptor)。每个描述符占8个字节,它的格式如图10‑7所示。
31 |
|
|
|
|
| 2 | 1 0 |
描述符表地址 [31..2] | 保留 |
图10-6 物理区域描述符表指针寄存器
31 | 16 | 15 | 1 | 0 |
| |||||||
| 缓冲区的物理地址[31..1] |
|
| |||||||||
| EOT | 保留 | 缓冲区的长度[15..1] |
| ||||||||
图10‑7 物理区域描述符的格式
每个描述符指出一个内存缓冲区的物理地址及缓冲区长度。物理地址的第0位和缓冲区长度的第0位必须为0。当缓冲区长度为0时,传送65536个字节。EOT位代表这个缓冲区是否为最后一个。如果有多个内存缓冲区,前面几个缓冲区的描述符中的EOT为0,而最后一个为1。
例如,图10‑8中有2个不连续的内存缓冲区,地址分别为00060000H、00063000H,长度都为200H。可以通过一次DMA操作将这2个缓冲区的内容传送给设备,写到硬盘上的2个连续的扇区。这就需要构造一个描述符表,表中的2项描述符分别描述一个物理区域(起始地址和长度),第2项的EOT位设为0。设描述符表的地址为00070000H,将描述符表的地址写入描述符表指针寄存器,在执行DMA操作时,PCI-IDE控制器就能够获得内存中的2个缓冲区的地址和长度。
00070000H |
| 00060000H |
| 第1个缓冲区 | 00060000H |
描述符表指针寄存器 |
| 00000200H |
|
| |
| 00063000H |
|
| ||
| 80000200H |
|
|
| |
| 描述符表 |
| 第2个缓冲区 | 00063000H | |
|
|
| |||
|
|
| |||
|
|
|
|
图10‑8 不连续缓冲区
(4)端口地址分配
PCI-IDE控制器支持2个IDE通道(主/Primary通道和次/Secondary通道),每个通道分配了8个字节的端口地址,从基地址寄存器5(BAR5)开始,一共16个地址。主控命令寄存器、主控状态寄存器和描述符指针寄存器的端口地址如表10-1所示。
表10‑1 PCI-IDE控制器的寄存器分配
主通道 |
| 次通道 | ||||
端口地址 | 寄存器名称 | 实例 |
| 端口地址 | 寄存器名称 | 实例 |
BAR5+00H | 主控命令寄存器 | 0C000H |
| BAR5+08H | 主控命令寄存器 | 0C008H |
BAR5+01H | 保留 | 0C001H |
| BAR5+09H | 保留 | 0C009H |
BAR5+02H | 主控状态寄存器 | 0C002H |
| BAR5+0AH | 主控状态寄存器 | 0C00AH |
BAR5+03H | 保留 | 0C003H |
| BAR5+0BH | 保留 | 0C00BH |
BAR5+04H | 物理区域描述符指针寄存器 | 0C004H |
| BAR5+0CH | 物理区域描述符指针寄存器 | 0C00CH |
2. PCI-IDE控制器的DMA
通过DMA读写硬盘的步骤为:
(1) 在内存中构造一个描述符表,指向缓冲区。
(2) 把描述符表的地址写入描述符表指针寄存器。
(3) 设置主控命令寄存器的R/W位。读硬盘时,设为1;写硬盘时,设为0。
(4) 将1写入主控状态寄存器的Interrupt和Error位,将这2个位复位为0。
(5) 要传输的扇区数、扇区地址等写入ATA设备寄存器。将命令码(如C8H、CAH)写入ATA命令寄存器。
(6) 将1写入主控命令寄存器的Start/Stop位。
(7) PCI-IDE控制器就会在内存缓冲区和硬盘之间进行DMA数据传输。
(8) 传输完毕后,硬盘发出一个中断请求。PCI-IDE控制器随之向CPU发出中断请求。
(9) 响应中断请求后,将0写入主控命令寄存器的Start/Stop位,将1写入主控状态寄存器的Interrupt位,以清除中断请求。
(10) 读取主控状态寄存器和ATA状态寄存器,确认命令是否成功。
下面的示例程序在VirtualBox的DOS环境下运行。如图10-9所示,VirtualBox的硬盘作为主通道上的主盘,即“第一IDE控制器”。因此,其ATA设备寄存器地址为(1F0~1F7H,3F6H)。
文件c:/asm/tool/vbox/HDD0.vdi作为VirtualBox的硬盘,硬盘大小设置为80MB。VirtualBox采用了动态分配技术,HDD0.vdi并没有占据80MB空间,在客户机向硬盘写入数据时,该文件会逐渐增大。
硬盘作为主盘,因此程序在写入“设备/磁头寄存器”时,DEV被设置为0。
图10‑9 VirtualBox的硬盘设置
程序中使用的主要端口为:
(1) PCI-IDE的3个寄存器:主控命令寄存器(端口地址0C000H,8位)、主控状态寄存器(端口地址0C002H,8位)、物理区域描述符指针寄存器(端口地址0C004H,32位)。
(2) ATA设备寄存器(1F0~1F7H,3F6H)。
该程序向硬盘发送0C8h命令(READ DMA),该命令所要求的其他寄存器格式如图10-10所示。
图10‑10 ATA规范中的READ DMA命令
为简化起见,程序没有采用中断,而是将CPU中的IF位清0。在DMA传输完成时,PCI-IDE控制器向CPU发出的中断请求被屏蔽。程序通过检查主控状态寄存器的Interrupt位是否为1,来确定DMA是否传输完成。
;程序清单: hdddma-r.asm(实模式下的硬盘DMA)
.386P
bmcr_base_addr EQU 0C000H ; DMA主控寄存器首地址
numSect EQU 1 ; 读取1个扇区
lbaSector EQU 0 ; LBA=0
BM_COMMAND_REG EQU 0 ; 主控命令寄存器的偏移
BM_STATUS_REG EQU 2 ; 主控状态寄存器的偏移
BM_PRD_ADDR_REG EQU 4 ; 物理区域描述符指针寄存器的偏移
pio_base_addr1 EQU 01F0H ; ATA设备控制块寄存器基地址
pio_base_addr2 EQU 03F0H ; ATA命令命令块寄存器基地址
DSEG SEGMENT USE16 ; 16位数据段
ALIGN 2
_Buffer db 512*numSect dup (0) ; 内存缓冲区
_BufferLen equ $-_Buffer
ALIGN 4
prdBuf dd 0 ; 物理区域描述符
dd 0
prdBufAddr dd 0 ; 物理区域描述符地址
bufferaddr dd 0 ; 内存缓冲区地址
DSEG ENDS ; 数据段结束
SSEG SEGMENT PARA STACK ; 堆栈段
DB 512 DUP (0)
SSEG ENDS ; 堆栈段结束
outx MACRO Reg, Val ; 向Reg端口写入数据Val
mov dx, Reg
mov al, Val
out dx, al
ENDM
inx MACRO Reg ; 从Reg端口读入数据, 存放在AL中
mov dx, Reg
in al, dx
ENDM
CSEG SEGMENT USE16 ; 代码段
ASSUME CS:CSEG,DS:DSEG
; 检查ATA状态寄存器, 直到BSY=0和DRQ=0
waitDeviceReady proc
waitReady:
inx pio_base_addr1+7 ; 读取ATA状态寄存器
and al, 10001000b ; BSY=1或DRQ=1,继续查询
jnz waitReady
ret
waitDeviceReady endp
; 采用DMA方式读取硬盘扇区
ReadSectors proc
; Start/Stop=0, 停止以前的DMA传输
outx bmcr_base_addr+BM_COMMAND_REG, 00h
; 清除主控状态寄存器的Interrupt和Error位
outx bmcr_base_addr+BM_STATUS_REG, 00000110b
; 建立一个物理区域描述符
mov eax, bufferaddr
mov prdBuf, eax ; Physical Address
mov word ptr prdBuf+4, _BufferLen ; Byte Count [15:1]
mov word ptr prdBuf+6, 8000h ; EOT=1
; 物理区域描述符的地址写入PRDTR
mov eax, prdBufAddr
mov dx, bmcr_base_addr+BM_PRD_ADDR_REG
out dx, eax
; 主控命令寄存器的R/W=1, 表示写入内存(读取硬盘)
outx bmcr_base_addr+BM_COMMAND_REG, 08h
; 等待硬盘BSY=0和DRQ=0
call waitDeviceReady
; 设置设备/磁头寄存器的DEV=0
outx pio_base_addr1+6, 00h
; 等待硬盘BSY=0和DRQ=0
call waitDeviceReady
; 设备控制寄存器的nIEN=0, 允许中断
outx pio_base_addr2+6, 00
; 设置ATA寄存器
outx pio_base_addr1+1, 00h ; =00
outx pio_base_addr1+2, numSect ; 扇区号
outx pio_base_addr1+3, lbaSector >> 0 ; LBA第7~0位
outx pio_base_addr1+4, lbaSector >> 8 ; LBA第15~8位
outx pio_base_addr1+5, lbaSector >> 16 ; LBA第23~16位
; 设备/磁头寄存器:LBA=1, DEV=0, LBA第27~24位
outx pio_base_addr1+6, 01000000b or (lbaSector >> 24)
; 设置ATA命令寄存器
outx pio_base_addr1+7, 0C8h ; 0C8h=Read DMA
; 读取主控命令寄存器和主控状态寄存器
inx bmcr_base_addr + BM_COMMAND_REG
inx bmcr_base_addr + BM_STATUS_REG
; 主控命令寄存器的R/W=1,Start/Stop=1, 启动DMA传输
outx bmcr_base_addr+BM_COMMAND_REG, 09h
; 现在开始DMA数据传送
; 检查主控状态寄存器, Interrupt=1时,传送结束
mov ecx, 4000h
notAsserted:
inx bmcr_base_addr+BM_STATUS_REG
and al, 00000100b
jz notAsserted
; 清除主控状态寄存器的Interrupt位
outx bmcr_base_addr+BM_STATUS_REG, 00000100b
; 读取主控状态寄存器
inx bmcr_base_addr+BM_STATUS_REG
; 主控命令寄存器的Start/Stop=0, 结束DMA传输
outx bmcr_base_addr+BM_COMMAND_REG, 00h
ret
ReadSectors endp
Start PROC
mov ax,DSEG
mov ds,ax ; ds指向数据段
mov es,ax ; es指向数据段
mov bx,16
mov ax,ds
mul bx ; 计算并设置数据段基址
add ax, offset prdBuf ; 数据段基址+offset prdBuf
adc dx, 0 ; dx:ax = prdBuf的物理地址
mov WORD PTR prdBufAddr, ax
mov WORD PTR prdBufAddr+2, dx
mov ax,ds
mul bx
add ax, offset _Buffer ; 段基址+offset _Buffer
adc dx, 0 ; dx:ax = _Buffer的物理地址
mov WORD PTR bufferaddr, ax
mov WORD PTR bufferaddr+2, dx
cli ; 关中断
call ReadSectors ; DMA方式读取硬盘扇区
sti ; 允许中断
call ShowBuffer ; 显示缓冲区内容
mov ax,4c00h
int 21h
Start ENDP
;字符显示宏指令的定义
EchoCh MACRO ascii
mov ah,2
mov dl,ascii
int 21h
ENDM
ShowBuffer PROC
lea si,_Buffer ; 显示_Buffer内容
cld
mov bp,_BufferLen/16
NextLine: mov cx,16
NextCh: lodsb
push ax
shr al,4
call ToASCII
EchoCh al
pop ax
call ToASCII
EchoCh al
EchoCh ' '
loop NextCh
EchoCh 0dh
EchoCh 0ah
dec bp
jnz NextLine
ret
ShowBuffer ENDP
ToASCII PROC
and al,0fh
cmp al,10
jae Over10
add al,'0'
ret
Over10:
add al,'A'-10
ret
ToASCII ENDP
CSEG ENDS ; 代码段结束
END start
10.4 实验题:保护方式下的硬盘DMA传输实验
程序hdddma-r.asm工作在实模式下,未采用中断技术。在该程序的基础上,在保护模式下实现硬盘DMA传输,编写中断处理程序来检测DMA传输是否结束。
PCI-IDE控制器有2个IDE通道,主通道DMA传输结束后产生中断请求IRQ14,次通道产生中断请求IRQ15。
IRQ14、IRQ15连接在从片8259上,从片8259连接到主片8259的IRQ2。
要求:
1. 将hdddma-r.asm修改为保护模式运行,可结合interpt.asm进行;
2. 设置从片8259的中断类型号为28H~2FH。IRQ14对应的中断类型号为2EH。在IDT中为该中断设置处理程序;
3. 设置从片8259的OCW1,第6位必须为0,允许IRQ14。主片8259的OCW1,第2位必须为0,允许IRQ2。
4. IRQ14中断处理结束时,必须先后向从片8259、主片8259发送EOI命令。
5. 将c:/asm/tool/vbox/HDD0.vdi挂接在VirtualBox的第二IDE控制器的从盘,修改程序,使之能够以DMA方式读取硬盘扇区;
6. 修改程序,使之能够以DMA方式写入硬盘扇区。