实验10 硬盘DMA编程

来源:互联网 发布:c语言matlab混编 编辑:程序博客网 时间:2024/04/28 14:35
导读:
  新一篇: 实验11 多任务和多线程
  采用PIO方式访问硬盘,硬盘扇区数据的读写完全由CPU通过IN、OUT指令执行,一个扇区占512字节,需要256次I/O操作,占用CPU资源。读取硬盘扇区时,每次I/O操作包括一个IN指令和一个内存写操作,CPU先读取16位数据到AX中,再将16位数据写入到内存中。写入硬盘扇区时,每次I/O操作则包括一个内存读操作和一个OUT指令。
  使用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循环,读取的全部64个寄存器。
  (4) 如果func等于0,检查的“配置头类型”寄存器的第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,  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传输。
  7 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方式写入硬盘扇区。

本文转自
http://blog.csdn.net/leapme/archive/2008/04/11/2283300.aspx
原创粉丝点击