<x86>第八章的内容大概是編寫一個加載器,從磁盤加載前面寫的程式,然後跳轉到該程式中執行并輸出hello world。咱不急,不跨這麼大步子,先寫個程式從磁盤上讀資料吧~
我把要讀取的資料存放在磁盤第二個扇區,格式如下:前2個位元組記錄需要讀取的資料長度,以位元組計算,後面緊跟實際資料。
關于IDE接口寄存器的介紹,這裡轉一個連接配接,因為網上都轉自部落客的文章:http://blog.chinaunix.net/uid-20235103-id-1970841.html
DiskDataReg equ 0x01f0
DiskErrReg equ 0x01f1
DiskSectCntReg equ 0x01f2
DiskLoLBAAddr equ 0x01f3
DiskMeLBAAddr equ 0x01f4
DiskHiLBAAddr equ 0x01f5
DiskModReg equ 0x01f6
DiskCmdStatReg equ 0x01f7
DiskReadCmd equ 0x20
DiskWriteCmd equ 0x30
Arg1Off equ 0x04 ;第一個參數相對bp偏移
Arg2Off equ 0x06 ;第二個參數相對bp偏移
Arg3Off equ 0x08 ;第三個參數相對bp偏移
Arg4Off equ 0x0A ;第四個參數相對bp偏移
StackBase equ 0x0000
StackEnd equ 0x2000
DataBase equ 0x0300
DataEnd equ 0x1FFF
section code align=16 vstart=0x7c00
jmp entry
SetSectAddr:
;設定邏輯扇區的位址
push bp
mov bp,sp
push bx
;發扇區總數
mov dx,DiskSectCntReg
mov al,byte [bp+Arg4Off]
out dx,al
;發扇區位址
mov dx,DiskLoLBAAddr
mov al,byte [bp+Arg3Off]
out dx,al
mov dx,DiskMeLBAAddr
mov al,byte [bp+Arg3Off+1]
out dx,al
mov dx,DiskHiLBAAddr
mov al,byte [bp+Arg2Off]
out dx,al
mov dx,DiskModReg
mov al,byte [bp+Arg2Off+1]
out dx,al
pop bx
mov sp,bp
pop bp
ret
WaitDiskReady:
;等待磁盤就緒
mov dx,DiskCmdStatReg
.waits:
in al,dx
and al,0x88
cmp al,0x08
jnz .waits
;判斷是否有錯誤
mov dx,DiskErrReg
.getErr:
in al,dx
cmp al,0x00
;al不為0出錯
jnz .resume
;沒有出錯 傳回0
xor ax,ax
ret
.resume:
;出錯 傳回
mov ax,0x01
ret
ReadFromDisk:
call SetSectAddr
;發讀指令
mov dx,DiskCmdStatReg
mov al,DiskReadCmd
out dx,al
call WaitDiskReady
;讀取儲存在扇區上的資料的有效長度
mov dx,DiskDataReg
in ax,dx
shr ax,0x01
;比較要讀取資料的長度和有效資料的長度,取最短的
;磁盤上儲存的資料,長度按位元組計數,但是DiskDataReg
;端口是16位端口,每次讀取1字,是以循環次數需要除2
mov cx,ax
mov dx,DiskDataReg
.readw:
in ax,dx
mov [bx],ax
add bx,2
loop .readw
;代碼沒寫好 沒傳回位址 隻能自己構造傳回位址了
lea ax,[RetFromReadDisk]
push ax
ret
entry:
xor ax,ax
mov ax,DataBase
mov ds,ax
mov ax,StackBase
mov ss,ax
mov sp,StackEnd
;讀取扇區總數
push word 0x0001
;LBA位址的0-15位
push word 0x0001
;LBA位址的16-31位
push word 0xe000
;需要從扇區上讀取的最大位元組數
push word 0x0200
jmp ReadFromDisk
RetFromReadDisk:
jmp $
times 510-($-$$) db 0
db 0x55,0xaa
len dw 0x000c ;後面有效資料的長度
string db 'Hello world',0
程式第134行,定義0x55 0xaa使得産生的bin檔案正好撐滿一個扇區,後面在保留一些資料,那就擠到第二個扇區去了。
在我沒想出其他儲存資料到磁盤任意位置前就先這麼湊合吧
本來想完成寫磁盤的程式,沒成功,是以代碼有點不倫不類,比如130行,本可以用call調用子過程的,現在用了jmp代替。雖然完成了過程調用,但是沒有傳回位址,怎麼回來呢?
call指令會把下一條指令的位址壓入堆棧,即sp指向的記憶體存入傳回位址,然後sp=sp-2,然後跳轉到目标位址執行。