天天看點

仿照着寫個bootloader (二)

  <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,然後跳轉到目标位址執行。