原创 DOS下BIOS中断INT13H、IO端口直接编程读取IDE、SATA硬盘的参数和TC语言程序

2015-4-23 16:44 5416 18 18 分类: 工程师职场 文集: 学以致用

DOS下BIOS中断INT13H、IO端口直接编程读取IDE、SATA硬盘的参数和TC语言程序

wxleasyland@sina.com

2015.4

 

一、    背景

华硕笔记本电脑A45VD,640GB SATA机械硬盘放在硬盘位,120GB SSD SATA硬盘放在光驱位。BIOS中设置SATA为AHCI方式(不是IDE兼容方式)。

在WIN7看,SATA控制器是Intel 7 Series/C216 Chipset Family SATA AHCI Controller,硬件ID是VEN_8086&DEV_1E03&CC_010601,位置在PCI总线0,设备31,功能2。

640GB机械硬盘是在CHANNEL0(通道0,即安装在SATA控制器接口PORT0上)。

120GB SSD硬盘是在通道2(即安装在SATA控制器接口PORT2上)。

SSD设置成为启动盘。因为BIOS启动设置是SSD硬盘,故在DOS下,机械硬盘就排第2了,即磁盘号81H。

 

以下均是对640GB机械硬盘进行分析。

 

二、    INT 13H传统中断,AH=08H

(1)  读取驱动器参数

AH=08H

入口:

DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘

返回:

CF=1——操作失败,AH=状态代码

CF=0 成功

BL=01H — 360K

=02H — 1.2M

=03H — 720K

=04H — 1.44M

=05H   ??

=06H  2.88M

=10H  ATAPI可移动介质

CH=最大柱面号的低8位   low eight bits of maximum cylinder number(柱面号从0开始算)

CL的位7-6=最大柱面号的高2位 high two bits of maximum cylinder number

CL的位5-0=最大扇区号 maximum sector number(扇区号从1开始算)

DH=最大磁头号 maximum head number(磁头号从0开始算)

DL=驱动器数 number of drives

ES:DI=磁盘驱动器参数表地址(只软驱)

 

(2) 注意:

1. 得到的数值是“编号”、不是“总个数”!

“总个数”是number of ...

“编号”是xxx number

扇区号从1开始,故扇区数=最大扇区号

磁头号从0开始,故磁头数=最大磁头号+1

 

2. BIOS保留最后1个柱用于测试目的,这个柱不报告出来,而且柱面号从0开始,所以   柱面总个数=最大柱面号+2!

There is one "test cylinder", It is last cylinder on drive, which is always reserved for test purposes, and therefore not reported by BIOS to be.(这点说明在http://computer-programming-forum.com/46-asm/16321e22892a8381.htm)

 

这两点说明看似简单,但是是经过研究很久才知道的!!!

 

(3)实测

U盘软驱启动,98版DOS下DEBUG,INT13H,AH=08H ,DL=81H硬盘:

得到AX=0,BX=0,CH=FE(1022,即0~1022+1柱号),CL=FF(63,即1~63扇号),DH=FE(254,即0~254头号),DL=2(即2个硬盘),ES:DI没有数据。

即1024柱×255头×63扇=16450560扇,合8.4G硬盘,总容量不一致。

 

可以看出,除柱面个数外,磁头数、扇区数与后面DISKMAN一致。

 

可以将柱面个数单独计算,柱面个数=实际LBA总扇区数÷头数÷每道扇区数,DISKMAN就是这么做的。

 

 

三、    INT 13H扩展中断,AH=48H

(1)获取磁盘参数

入口:

AH=48H

DL=驱动器号

DS:SI=指向缓冲区的指针(即返回的数据会保存在这个缓冲区中)

返回:

失败:

AH=错误号

CF置位

成功:

AH=0

CF=0

缓冲区中偏移量:

04H  双字   柱面总个数  ――注意:是“总个数”

08H  双字   物理磁头总个数

0CH  双字   物理每道扇区总个数

10H  4字     扇区总个数

18H  字      每扇区字节数

 

(2)实测

U盘软驱启动,98版DOS下DEBUG,INT13H扩展,AH=48H ,DL=81H硬盘,得到:

16383柱,16头,63扇,乘起来是16514064总扇区,即8.4G的硬盘,总容量不一致。

总扇区数是1250263728扇区,OK,总扇区数与AIDA,DISKMAN检测是一致的。

 

(3)分析

“INT13H扩展”返回的是16头,而“INT13传统”返回的是255头!!

“INT13H扩展”返回的直接就是硬盘的ATA参数,即与直接访问硬盘端口得到的参数是一样的!

而“INT13传统”返回的数值是BIOS加工转换后的,与分区表中的格式相一致。

ATA参数一般是16头最多了,而分区表中参数会是255头。也就是说,硬盘的ATA参数格式,与分区表参数格式是不一样的,所以造成了早期硬盘的一些容量限制问题。

 

 

四、    DISKMAN

用USB软驱启动到DOS,运行DISKMAN(打开时提示分区不对,忽略之),显示是“第2硬盘”。

 

DISKMAN得到的参数说明下:

DISKMAN的CMOS参数CHS,12289柱面,255磁头,63扇区,是按照“传统INT 13H” AH=08来得到的。

其中,柱面数是反算的,去掉小数,转成16进制,因变量只有2字节,只能保存低2字节,即1250263728总扇÷255头÷63扇=77825.32柱→0x13001,只能保存2字节,故是0x3001→12289柱。

“总扇区数”1250263728,是按“INT 13H扩展”得到的。

 

 

五、    分区表与“传统INT13H”兼容

分区表中,主分区和扩展分区必须要从新柱面开始(扩展分区中的逻辑盘不用)。

通过研究640GB机械硬盘上分区表中的分区起始LBA地址,看能不能被头、扇整除。发现分区是按255头、63扇来分的,而不是16头。即与“传统INT13H”兼容。

即分区软件分区时,要以“传统INT13H”得到的参数(柱面数除外)去进行分区。

 

 

六、    BIOS是支持SATA硬盘读写的

从前面可看出,BIOS是直接支持SATA硬盘的读写的,用“INT 13H扩展”访问即可!!因为主板厂家自己知道是用什么型号的SATA控制器,知道如何去控制它,并且它还要读取硬盘的MBR进行启动,所以BIOS必须支持SATA硬盘读写。所以DOS下直接用INT 13H即可读写SATA硬盘扇区,用GHOST软件克隆就不是问题了。

那为什么XP安装时,需要加载SATA驱动?因为WINDOWS不使用INT 13H去访问硬盘,而是自己要去底层访问SATA控制器,如果它不认识SATA控制器,就访问不了硬盘了。

 

 

 

七、    直接IO读取IDE硬盘

(1)ATA控制器、IDE硬盘

在电脑上,连接结构是这样的:

 

CPU<――>数据总线、地址总线、控制总线<――>PCI控制器<――>PCI总线<――>ATA控制器

                                                               ∧         ∧

                                                               |  硬盘线 |

                                                               ∨         ∨

                                                                IDE接口硬盘

 

硬盘IDE接口的数据线、控制线、地址线是分开的,IDE硬盘线有40芯之多,数据口直接挂在PCI的总线上,所以CPU可以直接访问到它。

ATA控制器是把IO端口地址转换到IDE硬盘接口上,从而CPU可以IN、OUT这些IO端口,直接访问到硬盘!

因此,IDE硬盘的IO端口是固定的,一组为命令寄存器组(Task File Registers),I/O的端口地址为1F0H~1F7H,其作用是传送命令与命令参数。另一组为控制/诊断寄存器(Control/Diagnostic Registers),I/O的端口地址为3F6H~3F7H,其作用是控制硬盘驱动器。

 

可以直接用单片机直接控制IDE硬盘:

单片机<――>40芯硬盘线<――>IDE硬盘

这个比较容易就能实现,将IDE接口的数据线、控制线、地址线分别连线控制就可以,物理电气信号连接不复杂。

 

注:ATA应是指协议标准,IDE应是指接口

相关详细说明可参考《如何在MCS-51系统中使用IDE硬盘》、《IDE接口硬盘读写技术》、《基于单片机的IDE硬盘控制的研究与设计》等文章。

 

 

(2)访问方法

引用别人说的(有修改):

①向端口3F6H写入控制字节,建立相应的硬盘控制方式;

②检验硬盘控制器和驱动器的状态(检测端口的第7和第6两位),如果控制器空闲而且驱动器就绪,即可输入命令;

③完整的输入7个字节长度的命令块,一次写入端口1F1H-1F7H,不论是否需要,端口1F1H-1F6H对应的6个字节的参数必须给出,端口1F7H的输出命令码为“0ECH”;

④检测端口1F7H的第7和第3两位,如果控制器空闲且第3位置1,表示操作结束,即可读取结果;

⑤通过端口1F0H读取数据,读取256个WORD到缓冲区,注意这里的数据是WORD 16位(2字节)的;

⑥再次读取端口1F7H,判断第0位是否为0,如果为0,表示命令成功,否则表示命令失败;

 

读出的256字节信息的主要内容如下:

偏移量          内 容               长度(字节)

00-01H    2字节

02H-03H      柱面总个数             2字节            

04-05H     2字节

06H-07H      磁头总个数             2字节            

08H-09H      每磁道所含的字节数     2字节            

0AH-0BH       每扇区所含的字节数     2字节            

0CH-0DH       每磁道所含的扇区个数   2字节            

0EH-13H   6字节

14H-27H      产品的序列号           20字节           

28H-29H   2字节

2AH-2BH       硬盘缓冲区容量         2字节            

2CH-2DH       ECC校验码的长度       2字节            

2EH-35H      硬件软体版本号         8字节            

36H-5DH      硬盘型号               40字节          

 

(3)程序

引用别人的(有修改),wxlhd1.c:

 

#include

#include

#include

#include

#include

 

#define BASEADDR1F0 0x1F0

#define BASEADDR3F6 0x3F6

 

void hdinfor2()   

{

    unsigned int i,j;

    unsigned char p;

 

    j=0;

    p=0;

    while (p!=0x40)          /*  必须RDY,并且不忙。读1f7H状态寄存器  0100 0000  bit6=1表示RDY*/

    {

       j++;

      if (j>=255) { printf("error1! NOT RDY & NOT BSY.   ERR REG=%02X", inportb(BASEADDR1F0+1));  exit(0);}

       p=inportb(BASEADDR1F0+7);

       printf("%02X ",p);

       p&=0xc0;       /*  1100 0000 bit6=1表示RDY,BIT7=1是忙*/

    }

    printf("ok1 \n");

 

    outportb(BASEADDR3F6,0);   /* 写3F6H 设备控制寄存器   0000 0000 BIT1中断允许  BIT2非复位*/

 

    for(i=0;i<5;i++)  outportb(BASEADDR1F0+1+i,0);    /* 写 1F1H~1F5H */

    outportb(BASEADDR1F0+6, 0xa0);         /*  驱动器/磁头寄存器 1010 0000  BIT6=0非LBA方式 */

    outportb(BASEADDR1F0+7, 0xec);         /*  写命令寄存器 */

 

    j=0;

    p=0;

    while(p!=0x8)              /*必须有中断请求产生,并且不忙*/

    {

       j++;

      if (j>=255) { printf("error2! NOT RDY & NOT IRQ.   ERR REG=%02X", inportb(BASEADDR1F0+1));  exit(0);} 

       p=inportb(BASEADDR1F0+7);    

       printf("%02X ",p);

       p&=0x88;          /* 1000 1000 BIT3=1是有中断请求,BIT7=1是忙 */

    }

    printf("ok2 \n");

 

  /* 读1F0H数据寄存器 */

    p=0;

    for(i=0; i<256; i++)

    {

       j=inport(BASEADDR1F0);        

       /* 应该要一次读出2字节,而不是1字节! 所以用inport 。

       硬盘中就是高字节在后, inport读出来就是高字节放在高字节上。

       比如硬盘中是11 22,inport读出来就是0x2211

       */

       printf("%02X %02X ",(unsigned char) j, (unsigned char) (j>>8) );

       p++;

       if(p>=8)

       {

           /*printf("\n");*/

           p=0;

       }

      

    }

 

    return;

}

 

 

void main()

{

    hdinfor2();

}

 

 

(4)DOS下运行

在DOS下运行,肯定失败,因为这里是SATA AHCI控制器,没有传统的IDE端口。

在虚拟机下DOS运行,OK,成功。

 

 

 

八、    直接读取SATA硬盘之(一)、SATA AHCI控制器、SATA硬盘

 

在电脑上,连接结构是这样的:

 

CPU<――>数据总线、地址总线、控制总线<――>PCI控制器<――>PCI总线<――>AHCI控制器<――>串行硬盘线<――>SATA硬盘

 

SATA硬盘没有直接挂在PCI的总线上,所以CPU无法直接访问到它,CPU只能访问AHCI控制器。

AHCI控制器与SATA硬盘之间是通过SATA协议进行通讯的,这个可以不用管它,只需要考虑如何访问AHCI控制器即可。

也就是说,AHCI控制器把SATA接口封装掉了,内部完成与SATA硬盘之间的电气信号连接、协议通讯。我们只需要访问AHCI控制器,大大简化了操作。

 

PCI转SATA口的转换卡,也能接SATA硬盘,上面也是带了控制器,原理是一样的,不做研究。

 

如果用单片机直接控制SATA硬盘:

单片机<――>串行硬盘线<――>SATA硬盘

个人认为这几乎是不可能的事情,串行信号速率太高了,物理电气信号连接单片机很困难,更何况SATA通讯协议又很复杂。

所以要接一个控制器,让控制器来代劳。

单片机<――>控制器<――>串行硬盘线<――>SATA硬盘

控制器当然不限于AHCI控制器这么复杂的东东,可以是转成简单一些的,比如USB To SATA接口芯片、SATA转IDE的转换卡等等,都可以。(个人观点)

 

AHCI控制器是一个PCI设备,访问起来比较复杂。需要先得到它的“PCI配置空间”信息,从而得到控制器的“内存空间”地址,再对“内存空间”进行读写,达到访问AHCI控制器的目的。

 

 

 

 

 

 

 

文章评论0条评论)

登录后参与讨论
我要评论
0
18
关闭 站长推荐上一条 /2 下一条