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条评论)
登录后参与讨论