自己動手從零寫桌面操作系統GrapeOS系列教程——20.彙編語言讀硬盤實戰

學習操作系統原理最好的方法是自己寫一個簡單的操作系統。


本講我們設計一個簡單的讀硬盤實驗。通過一定的方法使硬盤第二個扇區的前3個字節依次爲1、2、3,最後3個字節依次爲3、2、1,中間的506個字節全爲0。然後通過讀硬盤程序將硬盤第二個扇區的數據讀取到內存0x7e00-0x7fff的地方,也就是內存中MBR之後的512個字節。最後通過QEMU+DGB調試的方式來查看內存中0x7e00-0x7fff的數據,是否與硬盤第二個扇區中的數據一致,如果一致說明讀硬盤成功。
本講代碼文件共2個:

  • data1.asm
  • boot1.asm

下面我們開始實驗。

一、設置硬盤第二個扇區中的數據

data1.asm代碼如下:

db 1
db 2
db 3
times 506 db 0
db 3
db 2
db 1

data1.asm就是生成512字節的數據,前3個字節依次是1、2、3,最後3個字節依次爲3、2、1,中間的506個字節全爲0。
在PowerShell中輸入如下命令:

nasm data1.asm -o data1.bin
hexdump data1.bin -C

上述命令是將data1.asm通過彙編器生成了二進制文件data1.bin,然後通過hexdump命令查看data1.bin是否正確。截圖如下:

從上面截圖可以看到,生成的data.bin文件共512字節,其中前3個字節依次爲1、2、3,最後3個字節依次爲3、2、1,中間的506個字節全爲0。下面將data1.bin文件寫入到虛擬硬盤的第二個扇區中。
在PowerShell中輸入如下命令:

dd conv=notrunc if=data1.bin of=/media/VMShare/GrapeOS.img seek=1
hexdump /media/VMShare/GrapeOS.img -C

上面dd命令中的參數seek=1意思是在寫入GrapeOS.img時,跳過1個寫入塊,寫入塊默認大小爲512字節,也就將data1.bin寫入到虛擬硬盤GrapeOS.img的第二個扇區中。截圖如下:

從上面截圖中可以看到,虛擬硬盤第二個扇區中的數據達到了我們的實驗要求。

二、讀硬盤程序

boot1.asm的代碼如下:

;定義常量
DISK_BUFFER equ 0x7e00 ;讀硬盤臨時存放數據用的緩存區,放到boot程序之後。

org 0x7c00

;初始化段寄存器
mov ax,cs
mov ds,ax ;ds指向與cs相同的段

mov esi,1 ;讀取硬盤的第2個扇區
mov di,DISK_BUFFER
call func_read_one_sector

stop:
hlt
jmp stop 

;讀取硬盤1個扇區(主硬盤控制器主盤)
;輸入參數:esi,ds:di。
;esi LBA扇區號
;ds:di 將數據寫入到的內存起始地址
;輸出參數:無。
func_read_one_sector:
;第1步:檢查硬盤控制器狀態
mov dx,0x1f7
.not_ready1:
nop ;nop相當於稍息 hlt相當於睡覺
in al,dx ;讀0x1f7端口
and al,0xc0 ;第7位爲1表示硬盤忙,第6位爲1表示硬盤控制器已準備好,正在等待指令。
cmp al,0x40 ;當第7位爲0,且第6位爲1,則進入下一個步。
jne .not_ready1 ;若未準備好,則繼續判斷。
;第2步:設置要讀取的扇區數
mov dx,0x1f2
mov al,1
out dx,al ;讀取1個扇區
;第3步:將LBA地址存入0x1f3-0x1f6
mov eax,esi
;LBA地址7-0位寫入端口0x1f3
mov dx,0x1f3
out dx,al
;LBA地址15-8位寫入端口寫入0x1f4
shr eax,8
mov dx,0x1f4
out dx,al
;LBA地址23-16位寫入端口0x1f5
shr eax,8
mov dx,0x1f5
out dx,al
;第4步:設置device端口
shr eax,8
and al,0x0f ;LBA第24-27位
or al,0xe0 ;設置7-4位爲1110,表示LBA模式,主盤
mov dx,0x1f6
out dx,al
;第5步:向0x1f7端口寫入讀命令0x20
mov dx,0x1f7
mov al,0x20
out dx,al
;第6步:檢測硬盤狀態
.not_ready2:
nop ;nop相當於稍息 hlt相當於睡覺
in al,dx ;讀0x1f7端口
and al,0x88 ;第7位爲1表示硬盤忙,第3位爲1表示硬盤控制器已準備好數據傳輸。
cmp al,0x08 ;當第7位爲0,且第3位爲1,進入下一步。
jne .not_ready2 ;若未準備好,則繼續判斷。
;第7步:從0x1f0端口讀數據
mov cx,256 ;每次讀取2字節,一個扇區需要讀256次。
mov dx,0x1f0
.go_on_read:
in ax,dx
mov [di],ax
add di,2
loop .go_on_read
ret

times 510-($-$$) db 0
db 0x55,0xaa

上面的代碼主要就是讀硬盤函數func_read_one_sector,總共分7個步驟,與上講中的讀硬盤操作步驟一致。結合代碼註釋和之前講的知識,大家應該是可以看懂的。
下面我們將編譯boot1.asm,並將生成的二進制文件寫入到虛擬硬盤的第一個扇區。在PowerShell中輸入如下命令:

nasm boot1.asm -o boot1.bin
dd conv=notrunc if=boot1.bin of=/media/VMShare/GrapeOS.img

截圖如下:

在Windows的cmd命令行中啓動QEMU調試模式:

qemu-system-i386 d:\GrapeOS\VMShare\GrapeOS.img -S -s

截圖如下:

在PowerShell中啓動GDB:

[root@CentOS7 Lesson20]# gdb
(gdb) target remote 192.168.10.102:1234
(gdb) b *0x7c00
(gdb) c
Continuing.

Breakpoint 1, 0x00007c00 in ?? ()
(gdb) x /512xb 0x7e00

截圖如下:

從截圖中可以看到,此時程序停在了斷點0x7c00處,此時內存中0x7e00-0x7fff中的數據都是0。
我們輸入命令c,讓程序繼續運行幾秒鐘,然後Ctrl+C,讓程序暫停。此時讀硬盤程序應該已經執行完了,我們再來查看一下內存0x7e00-0x7fff中的數據。截圖如下:

從截圖中可以看到,此時內存中0x7e00-0x7fff的數據和硬盤第二扇區中的數據一致,說明讀取成功,實驗完畢。


本講視頻版地址:https://www.bilibili.com/video/BV1JY411z7VT/
配套的代碼與資料在:https://gitee.com/jackchengyujia/grapeos-course
GrapeOS操作系統交流QQ羣:643474045

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章