www.pudn.com > diskdirectIO.zip > 硬盘直接IO.asm, change:2007-07-29,size:10517b


通过IO口对硬盘绝对扇区读写2006-09-20 17:18对硬盘进行操作的常用端口是1f0h~1f7h号端口,各端口含义如下: 
端口号     读还是写   具体含义 
1F0H       读/写      用来传送读/写的数据(其内容是正在传输的一个字节的数据) 
1F1H       读         用来读取错误码 
1F2H       读/写      用来放入要读写的扇区数量 
1F3H       读/写      用来放入要读写的扇区号码 
1F4H       读/写      用来存放读写柱面的低8位字节 
1F5H       读/写      用来存放读写柱面的高2位字节(其高6位恒为0) 
1F6H       读/写      用来存放要读/写的磁盘号及磁头号 
                    第7位     恒为1 
                    第6位     恒为0 
                    第5位     恒为1 
                    第4位     为0代表第一块硬盘、为1代表第二块硬盘 
                    第3~0位    用来存放要读/写的磁头号 
1f7H       读         用来存放读操作后的状态 
                    第7位     控制器忙碌 
                    第6位     磁盘驱动器准备好了 
                    第5位     写入错误 
                    第4位     搜索完成 
                    第3位     为1时扇区缓冲区没有准备好 
                    第2位     是否正确读取磁盘数据 
                    第1位     磁盘每转一周将此位设为1, 
                    第0位     之前的命令因发生错误而结束 
         写         该位端口为命令端口,用来发出指定命令 
                    为50h     格式化磁道 
                    为20h     尝试读取扇区 
                    为21h     无须验证扇区是否准备好而直接读扇区 
                    为22h     尝试读取长扇区(用于早期的硬盘,每扇可能不是512字节,而是128字节到1024之间的值) 
                    为23h     无须验证扇区是否准备好而直接读长扇区 
                    为30h     尝试写扇区 
                    为31h     无须验证扇区是否准备好而直接写扇区 
                    为32h     尝试写长扇区 
                    为33h     无须验证扇区是否准备好而直接写长扇区 
注:当然看完这个表你会发现,这种读写端口的方法其实是基于磁头、柱面、扇区的硬盘读写方法,不过大于8G的硬盘的读写方法也是通过端口1F0H~1F7H来实现的^_^ 
 
 
一个通过对硬盘输入输出端口操作来读写硬盘的实例 
让我们来看一个关于INT13H读写硬盘程序实例。在例子中详细说明了硬盘的读写操作所用到的端口,并且把通过INT13H读出的主引导区得到的数据和通过输入输出读主引导区得到的数据进行比较,从而证实这两种操作功能相同,程序片段如下: 
 
mov     dx,1f6h         ; 要读入的磁盘号及磁头号 
mov     al,0a0h         ;磁盘0,磁头0    
out      dx,al 
 
mov     dx,1f2h         ;要读入的扇区数量 
mov     al,1            ;读一个扇区     
out      dx,al  
 
mov     dx,1f3h         ;要读的扇区号  
mov     al,1            ;扇区号为1 
out      dx,al 
 
mov     dx,1f4h         ;要读的柱面的低8位 
mov     al,0            ; 柱面低8位为0  
out     dx,al 
 
mov     dx,1f5h         ; 柱面高2位   
mov     al,0            ; 柱面高2位为0(通过1F4H和1F5H端口我们可以确定 
; 用来读的柱面号是0) 
out      dx,al 
 
mov     dx,1f7h         ;命令端口 
mov     al,20h          ; 尝试读取扇区 
out      dx,al 
still_going: 
in      al,dx 
test     al,8            ;扇区缓冲是否准备好 
jz     still_going     ;如果扇区缓冲没有准备好的话则跳转,直到准备好才向下执行。 
 
mov     cx,512/2        ;设置循环次数(512/2次) 
mov     di,offset buffer 
mov     dx,1f0h         ;将要传输的一个字节的数据 
rep      insw            ;传输数据 
 
;   ------ 
 
mov     ax,201h         ;以下是用INT13H读硬盘的0磁头、0柱面、1扇区 
mov     dx,80h 
mov     cx,1 
mov     bx,offset buffer2 
int       13h 
 
mov     cx,512         ;以下部分用来比较2种方法读出的硬盘数据 
mov     si,offset buffer 
mov     di,offset buffer2 
repe     cmpsb 
jne      failure 
mov     ah,9 
mov     dx,offset readmsg 
int      21h 
jmp     good_exit 
failure: 
mov     ah,9 
mov     dx,offset failmsg 
int       21h 
good_exit:              ;以下部分用来结束程序 
mov      ax,4c00h        ;退出程序 
int      21h 
 
readmsg db      'The buffers match.  Hard disk read using ports.$' 
failmsg db      'The buffers do not match.$' 
buffer  db      512 dup ('V') 
buffer2 db      512 dup ('L') 
 
 
附: 
我的“硬盘绝对扇区检测”功能代码: 
平台:Fedora Core  5 
            NASM version 0.98.39 
             gcc 版本 4.1.0 20060304 (Red Hat 4.1.0-3)                   
编译命令: 
            由于有端口读写,所以需要在root下运行 
             nasm  -f  elf  *.asm 
             gcc       *.o 
             ./a.out 
代码: 
global main 
extern printf  
extern ioperm 
section .data 
dmsg  db 'The buffers match. The sector is good.',0Dh,0Ah,0 
failmsg db 'The buffers do not match. The sector is bad.',0Dh,0Ah,0 
buf_d times 512 db  'V' 
ct1 db 'buf_director',0Dh,0Ah,0 
buf_s times 512 db 'K' 
ct2 db 'buf_source',0Dh,0Ah,0 
buf_g times 512 db 'G' 
ct3 db 'buf_ghost',0Dh,0Ah,0 
disk db 0b0h  ;要读入的磁盘号及磁头号 
;第7位     恒为1 
;第6位     恒为0 
;第5位     恒为1 
;第4位     为0代表第一块硬盘、为1代表第二块硬盘 
;第3~0位    用来存放要读/写的磁头号 
secnum db 1 ;要读入的扇区数量,读一个扇区 
secno db 1 ;要读的扇区号,扇区号为1 
cylin_l db 0 ;要读的柱面的低8位,柱面低8位为0 
cylin_h db 0 ;柱面高2位,柱面高2位为0 
section .text 
main: 
mov ax,ds 
mov es,ax 
 
push word 1 ;打开 1f0h-1f7h 端口的读写权限 
push dword 08h 
push dword 1f0h 
call ioperm 
add esp,10 ;清空栈  
 
push dword buf_g ;备份数据 
pop eax 
mov edi,eax 
call read 
        push    dword buf_g ;打印 
        call    printf 
        pop     eax 
 
push dword buf_s ;写入数据  
pop eax 
mov esi,eax 
call write 
        push    dword buf_s ;打印 
        call    printf 
        pop     eax 
 
push dword buf_d ;读出数据 
pop eax 
mov edi,eax 
call read 
        push    dword buf_d ;打印 
        call    printf 
        pop     eax 
 
 
push dword buf_g ;恢复数据  
pop eax 
mov esi,eax 
call write 
 
mov cx,512 ;比较数据,测试是否坏道 
push dword buf_d 
pop eax 
mov esi,eax 
push dword buf_s 
pop eax 
mov edi,eax 
repe cmpsb 
jne failure 
 
push dword dmsg  ;成功,无坏块  
call printf 
pop eax 
jmp exit 
failure: 
push dword failmsg  ;失败,坏块 
call printf 
pop eax 
exit: 
ret 
read: 
mov dx,1f6h         ;要读入的磁盘号及磁头号 
mov al,[disk]    
out dx,al 
 
mov dx,1f2h         ;要读入的扇区数量 
mov al,[secnum]     
out dx,al  
 
mov dx,1f3h         ;要读的扇区号  
mov al,[secno] 
out dx,al 
 
mov dx,1f4h         ;要读的柱面的低8位 
mov al,[cylin_l] 
out dx,al 
 
mov dx,1f5h         ;柱面高2位   
mov al,[cylin_h] 
out dx,al 
 
mov dx,1f7h         ;命令端口 
mov al,20h          ; 尝试读取扇区 
;1f7H    读         用来存放读操作后的状态 
;第7位     控制器忙碌 
                     ;第6位     磁盘驱动器准备好了 
                     ;第5位     写入错误 
                    ;第4位     搜索完成 
                     ;第3位     为1时扇区缓冲区没有准备好 
                     ;第2位     是否正确读取磁盘数据 
                    ;第1位     磁盘每转一周将此位设为1, 
                     ;第0位     之前的命令因发生错误而结束 
         ;写         该位端口为命令端口,用来发出指定命令 
                     ;为50h     格式化磁道 
                     ;为20h     尝试读取扇区 
                     ;为21h     无须验证扇区是否准备好而直接读扇区 
                     ;为22h     尝试读取长扇区(用于早期的硬盘,每扇可能不是512字节,而是128字节到1024之间的值) 
                     ;为23h     无须验证扇区是否准备好而直接读长扇区 
                     ;为30h     尝试写扇区 
                     ;为31h     无须验证扇区是否准备好而直接写扇区 
                     ;为32h     尝试写长扇区 
                     ;为33h     无须验证扇区是否准备好而直接写长扇区 
out dx,al 
still_going: 
in al,dx 
test al,8            ;扇区缓冲是否准备好 
jz still_going 
 
mov     cx,512/2        ;设置循环次数(512/2次) 
mov dx,1f0h         ;将要传输的一个字节的数据 
rep insw           ;传输数据 
ret 
 
write: 
        mov     dx,1f6h         ;要写入的磁盘号及磁头号 
        mov     al,[disk] 
        out     dx,al 
 
        mov     dx,1f2h         ;要写入的扇区数量 
        mov     al,[secnum] 
        out     dx,al 
 
        mov     dx,1f3h         ;要写的扇区号 
        mov     al,[secno] 
        out     dx,al 
 
        mov     dx,1f4h         ;要写的柱面的低8位 
        mov     al,[cylin_l] 
        out     dx,al 
 
        mov     dx,1f5h         ;柱面高2位 
        mov     al,[cylin_h] 
        out     dx,al 
 
        mov     dx,1f7h         ;命令端口 
        mov     al,30h          ;尝试写入扇区 
        out     dx,al 
still_going_2: 
       in      al,dx 
       test    al,40h           ;第6位     磁盘驱动器是否准备好了 
       jz      still_going_2 
 
write_again: 
        mov     cx,512/2        ;设置循环次数(512/2次) 
        mov     dx,1f0h         ;将要传输的一个字节的数据 
        rep     outsw           ;传输数据 
 
; in al,dx 
; test al,20h ;第5位     写入错误 
; jz write_again 
ret 
 
附2: 
硬盘绝对扇区检测程序代码--shell 
 
#!/bin/bash 
#$1  起始扇区号,扇区号LBA从 0 开始计 
#$2 待测试扇区数 
#$3 测试次数 
 
 
if [ $# -ne 3 ] 
then 
echo "Useage: ./testdisk StartSectorNo  SectorNum  TestTimes" 
exit $E_BADARGS 
fi 
let "z = 65"   #初始写 'A' 
 
a=1 
while [ $a -le "$3" ] 
do 
        a=$(($a+1)) 
######################生成用来测试写入的文件######################################### 
let "t = $z / 64" 
        let "t = $t * 10 + ( $z % 64  / 8 " 
        let "t = t * 10 + $z % 8" 
        rm -f tmp* 
touch tmp 
        echo -ne "\0$t" >> ./tmp 
        let  "z += 1" 
if [ $z = 91 ] 
        then 
        let "z = 65" 
        fi 
        let "i = 1" 
        while [ $i -ne 10 ] 
        do 
touch tmp2 
                cat ./tmp >> ./tmp2 
                cat ./tmp >> ./tmp2 
                mv tmp2  tmp 
                let "i += 1" 
        done 
mv tmp tmp2 
let "i = 0" 
touch tmp 
while [ $i -ne $2 ] 
do 
cat ./tmp2 >> ./tmp 
let "i += 1" 
done 
############################开始测试写入&&读出&&比较#################################### 
dd if=./tmp of=/dev/hdb bs=512 count=$2 seek=$1 
dd if=/dev/hdb of=./tmp_b bs=512 count=$2 skip=$1 
if diff tmp tmp_b >> log 
then  
echo "***the sector from $1 to $[$1+$2-1] is good*****At the test times: $[$a-1]**********" 
else 
let "b = 0" 
while  [ $b -le $(($2-1)) ] 
do 
b=$(($b+1)) 
dd if=tmp2 of=/dev/hdb bs=512 count=1 seek=$[$1+$b-1] 
dd if=/dev/hdb of=./tmp_s bs=512 count=1 skip=$[$1+$b-1] 
        if diff tmp2 tmp_s >> log 
                 then 
                         echo "*******the sector $[$1+$b-1] is good*****At the test time: $[$a-1]************************" 
                 else 
                         echo "*******the sector $[$1+$b-1] is bad******At the test time: $[$a-1]************************" 
                 fi 
done 
fi 
done