自己動手從零寫桌面作業系統GrapeOS系列教學——21.組合語言寫硬碟實戰

2023-03-22 12:01:28

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


在上一講中我們學習了用組合語言讀硬碟,本講我們來學習用組合語言寫硬碟。同樣也是設計一個簡單的實驗,實驗內容為:

在記憶體中準備一段有特徵的512位元組資料,地址為0x7e00~0x7fff,其特徵是前3個位元組依次為4、5、6,最後3個位元組依次為6、5、4。然後將該段記憶體資料寫入到硬碟的第2個磁區,並檢視虛擬硬碟第2個磁區的資料是否與記憶體中0x7e00~0x7fff的資料一致,如果一致則說明寫硬碟成功。

本講程式碼檔案只有一個boot2.asm
boot2.asm程式碼如下:

;定義常數
DISK_BUFFER equ 0x7e00 ;臨時存放資料用的快取區,放到boot程式之後。0x7e00~0x7fff。

org 0x7c00

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

mov bx,DISK_BUFFER
;向快取區前3個位元組依次寫入4、5、6。
mov byte [bx+0],4
mov byte [bx+1],5
mov byte [bx+2],6
;向快取區最後3個位元組依次寫入6、5、4。
mov byte [bx+509],6
mov byte [bx+510],5
mov byte [bx+511],4

mov si,DISK_BUFFER
mov edi,1 ;寫入硬碟的第2個磁區
call func_write_one_sector

stop:
hlt
jmp stop 

;將記憶體中的512個位元組寫入到硬碟的一個指定磁區中(主硬碟控制器主盤)
;輸入引數:ds:si,edi。
;ds:si 資料來源記憶體地址
;edi LBA磁區號
;輸出引數:無。
func_write_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,edi
;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埠寫入寫命令0x30
mov dx,0x1f7
mov al,0x30
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_write:
mov ax,[si]
out dx,ax
add si,2
loop .go_on_write
ret

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

之前我們介紹過讀硬碟操作和寫硬碟操作都是7個步驟,其中只有第5步和第7步不同,其它步驟完全相同,大家可以和上一講中的程式碼對比看一下。
下面我們將boot2.asm編譯並寫入到虛擬硬碟的第一個磁區:

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

此時用hexdum命令檢視一下虛擬硬碟第二個磁區當前的資料,截圖如下:

從上面的截圖可以看到,此時虛擬硬碟第二個磁區前3個位元組依次為1、2、3,最後3個位元組依次為3、2、1。
下面我們以偵錯模式執行QEMU:

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

通過GDB連線到QEMU,直接輸入GDB命令c,讓程式執行幾秒鐘,然後Ctrl+C,讓程式暫停。此時寫硬碟程式應該已經執行完了。此時檢視一下記憶體0x7e00~0x7fff的資料:

(gdb) x /512xb 0x7e00

從上面截圖可以看到,在記憶體0x7e00~0x7fff的資料中,前3個位元組依次為4、5、6,最後3個位元組依次為6、5、4,其餘全是0。如果程式執行正確的話,此時硬碟第二磁區中的資料與此相同。
下面我們退出GDB,並關閉QEMU。然後用hexdum命令再檢視一下虛擬硬碟第二個磁區的資料,截圖如下:

從上面截圖中可以看到,硬碟第二磁區的資料與記憶體中0x7e00~0x7fff的資料一致,說明寫入成功,實驗完畢。


本講視訊版地址:https://www.bilibili.com/video/BV1Hk4y187Bw/
配套的程式碼與資料在:https://gitee.com/jackchengyujia/grapeos-course
GrapeOS作業系統交流QQ群:643474045