1. 程式人生 > >作業系統實驗一之載入程式

作業系統實驗一之載入程式

一, 實驗內容

改寫bootsect.s和setup.s, 完成如下主要功能:

1, bootsect.s能夠在螢幕上列印一段提示資訊"XXX is booting...", 其中XXX是你給自己的作業系統起的名字,例如LZJos、Sunix等.

2, bootsect.s能夠完成setup.s的載入, 並跳轉到setup.s開始地址執行. 

3, setup.s能夠像螢幕輸出一行資訊 "Now we are in SETUP" 

4, setup.s能獲取至少一個基本的硬體引數(如記憶體引數、顯示卡引數、硬碟引數等),將其存放在記憶體的特定地址,並輸出到螢幕上。

5,setup.s不再載入linux核心, 保持上述資訊顯示到螢幕上即可

二, 實驗基礎知識

1。啟動流程  
這裡寫圖片描述

三, 實驗步驟

1, 完成bootsect的螢幕輸出功能

  由於不需要載入linux核心,所以就不需要原始的linux程式碼那麼複雜,比如: 將bootsect自身移動到0x90000處等操作,可以忽略的。

  要顯示字串,那麼字串顯示到螢幕的哪裡呢?當然是當前游標的位置了!所以第一步就要先讀取游標的位置,這可以利用10號中斷的3號子程式來完成。要顯示字串,可以利用10號功能的13號子程式來完成,需要注意的是一定要邊顯示字元邊移動游標,最終的游標要移動到字串的末尾處。最後要注意用0xAA55來標記引導扇區。程式碼如下:

entry _start
_start:
! 首先利用10號中斷的3號功能來讀取游標位置 mov ah,#0x03 xor bh,bh int 0x10 ! 再利用10號中斷的13號功能顯示字串 mov cx,#50 ! 加上回車和換行,字串一共包含50個字元,所以設定cx為50 mov bx,#0x0007 mov bp,#msg1 mov ax,#0x07c0 mov es,ax ! es:bp=顯示字串的地址 mov ax,#0x1301
int 0x10 Inf_loop: jmp Inf_loop ! 無限迴圈 ! msg1處放置要顯示的字串 msg1: .byte 13,10 ! 換行+回車 .ascii "AXF OS is booting, my name is Aixiangfei ..." .byte 13,10,13,10 ! 兩對換行+回車 ! 下面是啟動盤具有有效引導扇區的標誌. 僅供BIOS中的程式載入扇區時識別使用。 ! 它必須位於引導扇區的最後兩個位元組中. .org 510 boot_flag: .word 0xAA55 ! 引導扇區的標記就是0XAA55

編譯和執行: 進入~/oslab/linux-0.11/boot/目錄,編譯並連線:

as86 -0 -a -o bootsect.o bootsect.s
ld86 -0 -s -o bootsect bootsect.o

  引數說明:-0(注意:這是數字0,不是字母O)表示生成8086的16位目標程式,-a表示生成與GNU as和ld部分相容的程式碼,-s告訴連結器ld86去除最後生成的可執行檔案中的符號資訊。

  如果這兩個命令沒有任何輸出,說明編譯與連結都通過了。需要留意的生成的bootsect的大小是544位元組,而載入程式必須要正好佔用一個磁碟扇區,即512個位元組。造成多了32個位元組的原因是ld86產生的是Minix可執行檔案格式,這樣的可執行檔案處理文字段、資料段等部分以外,還包括一個Minix可執行檔案頭部。所以最後必須要把這多餘的32個位元組刪掉,可以用linux自帶的工具dd來完成:

dd bs=1 if=bootsect of=Image skip=32

  去掉這32個位元組後,將生成的檔案拷貝到linux-0.11目錄下,並一定要命名為“Image”(注意大小寫)。然後就可以run了

2, setup的載入

  首先要確定setup是在磁碟的0磁軌2扇區,linux 0.11中的setup佔了4個扇區,而我們最後要寫的setup顯然沒有那麼複雜,所以可以只用1個扇區就可以了。另外,由於bootsect位於0x7c00處,佔用512個位元組,所以可以將setup載入到0x7e00處。要想從磁碟中載入資料到記憶體,可以利用BIOS提供的13號中斷輕鬆完成。最後就直接用jumi指令跳轉到0x7e00處即可。對前面的bootsect.s擴充套件之後的完整程式碼如下:

SETUPLEN = 1
SETUPSEG = 0x07e0

entry _start
_start:
! 首先利用10號中斷的3號功能來讀取游標位置
    mov    ah,#0x03        
    xor    bh,bh            
    int    0x10

! 再利用10號中斷的13號功能顯示字串
    mov    cx,#50            ! 加上回車和換行,字串一共包含50個字元,所以設定cx為50
    mov    bx,#0x0007
    mov    bp,#msg1
    mov     ax,#0x07c0
    mov     es,ax            ! es:bp=顯示字串的地址
    mov    ax,#0x1301        
    int    0x10

load_setup:
    mov    dx,#0x0000        ! 設定驅動器和磁頭(drive 0, head 0): 軟盤0磁頭
    mov    cx,#0x0002        ! 設定扇區號和磁軌(sector 2, track 0):0磁頭、0磁軌、2扇區
    mov    bx,#0x0200        ! 設定讀入的記憶體地址:BOOTSEG+address = 512,偏移512位元組
    mov    ax,#0x0200+SETUPLEN    ! 設定讀入的扇區個數(service 2, nr of sectors),
                    ! SETUPLEN是讀入的扇區個數,Linux 0.11設定的是4,
                    ! 我們不需要那麼多,我們設定為1
    int    0x13            ! 應用0x13號BIOS中斷讀入1個setup.s扇區
    jnc    ok_load_setup        ! 讀入成功,跳轉到ok_load_setup: ok - continue
    mov    dx,#0x0000        ! 軟碟機、軟盤有問題才會執行到這裡
    mov    ax,#0x0000        ! 否則復位軟碟機
    int    0x13
    j    load_setup        ! 重新迴圈,再次嘗試讀取

ok_load_setup:
    jmpi    0,SETUPSEG

! msg1處放置要顯示的字串
msg1:
    .byte 13,10            ! 換行+回車
    .ascii "AXF OS is booting, my name is Aixiangfei ..."
    .byte 13,10,13,10        ! 兩對換行+回車

! 下面是啟動盤具有有效引導扇區的標誌. 僅供BIOS中的程式載入扇區時識別使用。
! 它必須位於引導扇區的最後兩個位元組中.
.org 510
boot_flag:
    .word 0xAA55    ! 引導扇區的標記就是0XAA55

3, 完成setup的螢幕輸出功能
  這個很簡單,跟bootsect是一樣的。程式碼如下:

entry _start
_start:
! 首先利用10號中斷的3號功能來讀取游標位置
    mov    ah,#0x03
    xor    bh,bh
    int    0x10

! 再利用10號中斷的13號功能顯示字串
    mov    cx,#26
    mov    bx,#0x0007
    mov    bp,#msg
    mov ax,cs
    mov    es,ax
    mov    ax,#0x1301
    int    0x10

Inf_loop:
    jmp Inf_loop            ! 無限迴圈

msg:
    .byte 13,10
    .ascii "Now we are in SETUP."
    .byte 13,10,13,10


.org 510
boot_flag:
    .word 0xAA55

  編譯和與執行:現在有兩個檔案都要編譯、連結。一個個手工編譯,效率低下,所以藉助Makefile是最佳方式。linux 0.11中Makefile檔案已經幫我們把這件事做好了。進入liux-0.11目錄後,使用命令:

$ make BootImage
  但是我們發現竟然出現了錯誤:
  這裡寫圖片描述
  原因:這是因為make根據Makefile的指引執行了tools/build.c,它是為生成整個核心的映象檔案而設計的,沒考慮我們只需要bootsect.s和setup.s的情況。build.c從命令列引數得到bootsect、setup和system核心的檔名,將三者做簡單的整理後一起寫入Image。其中system是第三個引數(argv[3])。當“make all”或者“makeall”的時候,這個引數傳過來的是正確的檔名,build.c會開啟它,將內容寫入Image。而“make BootImage”時,傳過來的是字串”none”。所以,修改build.c的思路就是當argv[3]是”none”的時候,只寫bootsect和setup,忽略所有與system有關的工作,或者在該寫system的位置都寫上“0”。
  修改build.c檔案很簡單,只需要把第178到183這幾行程式碼刪除即可!
這裡寫圖片描述
 
4, 讀取硬體引數

  這個部分是最複雜的了。需要列印的硬體資訊有:游標位置,記憶體大小,磁碟的柱面數,磁頭數,每磁軌的扇區數。在這個實驗中,這些引數的資訊可以儲存在記憶體中的任意位置,在linux 0.11中,這些引數資訊是被儲存到了0x90000處,所以不妨跟linux 0.11一樣。

(1)獲取硬體引數

  獲得游標位置資訊,這個很簡單,只需要呼叫13號中斷的3號子程式就可以得到,前面已經用過了的。

  獲得記憶體大小,可以呼叫用15號中斷的88號子程式得到,也很簡單。

  與磁碟相關的資訊稍微複雜一點,這些資訊被儲存在0x0000:0x0104地址處的16個位元組的中,這16個位元組的資訊叫做“磁碟引數表”。所以獲得磁碟資訊的方法就是複製資料。

(2)數字轉字元

  現在已經將這些硬體引數取出來放在了0x90000處,接下來的工作是將這些引數顯示在螢幕上。這些引數都是一些無符號整數,所以需要做的主要工作是用匯程式設計序在螢幕上將這些整數用16進位制的形式顯示出來。

  因為十六進位制與二進位制有很好的對應關係(每4位二進位制數和1位十六進位制數存在一一對應關係),顯示時只需將原二進位制數每4位劃成一組,按組求對應的ASCII碼送顯示器即可。ASCII碼與十六進位制數字的對應關係為:0x30~0x39對應數字0~9,0x41~0x46對應數字a~f。從數字9到a,其ASCII碼間隔了7h,這一點在轉換時要特別注意。為使一個十六進位制數能按高位到低位依次顯示,實際程式設計中,需對bx中的數每次迴圈左移一組(4位二進位制),然後遮蔽掉當前高12位,對當前餘下的4位(即1位十六進位制數)求其ASCII碼,要判斷它是0~9還是a~f,是前者則加0x30得對應的ASCII碼,後者則要加0x37才行,最後送顯示器輸出。以上步驟重複4次,就可以完成bx中數以4位十六進位制的形式顯示出來。
  因為在輸出的時候需要呼叫多次,所以最好把這個功能寫成一個函式print_bx,方便使用。既然要用到函式,故一定要先設定好棧。為了方便,還可以寫一個函式print_nl實現換行的功能。
  最終setup.s程式碼如下:

INITSEG  = 0x9000   ! we move boot here - out of the way
SYSSEG   = 0x1000   ! system loaded at 0x10000 (65536).
SETUPSEG = 0x9020   ! this is the current segment
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text

entry start
start:

    mov ax,#SETUPSEG
    mov es,ax
! init ss:sp
        mov ax,#INITSEG
    mov ss,ax
    mov sp,#0xFF00
! *************print we are in step**************

        mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10

    mov cx,#28
    mov bx,#0x0007      ! page 0, attribute 7 (normal)
    mov bp,#msg_log
    mov ax,#0x1301      ! write string, move cursor
    int 0x10 

!*******************************hard parm******************************
    mov ax,#INITSEG ! this is done in bootsect already, but...
    mov ds,ax
    mov ah,#0x03    ! read cursor pos
    xor bh,bh
    int 0x10        ! save it in known place, con_init fetches
    mov [0],dx      ! it from 0x90000.
! Get memory size (extended mem, kB)
    mov ah,#0x88
    int 0x15
    mov [2],ax

! Get hd0 data

    mov ax,#0x0000
    mov ds,ax
    lds si,[4*0x41]
    mov ax,#INITSEG
    mov es,ax
    mov di,#0x0004
    mov cx,#0x10
    rep
    movsb

! *************************show msg

    mov ax,#SETUPSEG
    mov es,ax
    mov ax,#INITSEG ! this is done in bootsect already, but...
    mov ds,ax


! **************print cursor position***********
        mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10

    mov cx,#18
    mov bx,#0x0007      ! page 0, attribute 7 (normal)
    mov bp,#msg_cursor
    mov ax,#0x1301      ! write string, move cursor
    int 0x10 
! *****************cursor position**************
        mov ax,#INITSEG ! this is done in bootsect already, but...
    mov ds,ax
    mov dx,[0]
    call    print_hex 
! *******************print memory size****************       
        mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10

    mov cx,#14
    mov bx,#0x0007      ! page 0, attribute 7 (normal)
    mov bp,#msg_memory
    mov ax,#0x1301      ! write string, move cursor
    int 0x10 
! *****************memory size**************
        mov ax,#INITSEG ! this is done in bootsect already, but...
    mov ds,ax
    mov dx,[2]
    call    print_hex 
! *****************print KB************
    mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10

    mov cx,#2
    mov bx,#0x0007      ! page 0, attribute 7 (normal)
    mov bp,#msg7
    mov ax,#0x1301      ! write string, move cursor
    int 0x10 
! ****************print Cyles****************
    mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10

    mov cx,#8
    mov bx,#0x0007      ! page 0, attribute 7 (normal)
    mov bp,#msg_cyles
    mov ax,#0x1301      ! write string, move cursor
    int 0x10 
! ********************Cyles size************************
        mov ax,#INITSEG ! this is done in bootsect already, but...
    mov ds,ax
    mov dx,[4]
    call    print_hex 
! ****************print Heads****************
    mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10

    mov cx,#8
    mov bx,#0x0007      ! page 0, attribute 7 (normal)
    mov bp,#msg_heads
    mov ax,#0x1301      ! write string, move cursor
    int 0x10 
! ********************Heads size************************
        mov ax,#INITSEG ! this is done in bootsect already, but...
    mov ds,ax
    mov dx,[6]
    call    print_hex 
! ****************print msg_sectors****************
    mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10

    mov cx,#10
    mov bx,#0x0007      ! page 0, attribute 7 (normal)
    mov bp,#msg_sectors
    mov ax,#0x1301      ! write string, move cursor
    int 0x10 
! ********************msg_sectors size************************
        mov ax,#INITSEG ! this is done in bootsect already, but...
    mov ds,ax
    mov dx,[12]
    call    print_hex 

L6:
        jmp L6

print_hex:       
    mov    cx,#4        ! 4???????
print_digit:
    rol    dx,#4        ! ?????4???? !! ?dx??4?????4????
    mov    ax,#0xe0f    ! ah = ???????al = ???(4???)???
    and    al,dl        ! ?dl??4????
    add    al,#0x30     ! ?al????????0x30
    cmp    al,#0x3a
    jl     outp     !??????????
    add    al,#0x07     !?a?f????7
outp: 
    int    0x10
    loop   print_digit
    ret
print_nl:
    mov    ax,#0xe0d     ! CR
    int    0x10
    mov    al,#0xa     ! LF
    int    0x10
    ret

! ??wo are new in setup.s  ==28
msg_log:
    .byte 13,10
    .ascii "we are now in setup..."
    .byte 13,10,13,10
msg_cursor:
        .byte 13,10
    .ascii "Cursor position:"
msg_memory:
        .byte 13,10
    .ascii "Memory Size:"
msg_cyles:
        .byte 13,10
    .ascii "Cyles:"

msg_heads:
        .byte 13,10
    .ascii "Heads:"
msg_sectors:
        .byte 13,10
    .ascii "Sectors:"
msg7:
        .ascii "KB"

.text
endtext:
.data
enddata:
.bss
endbss:

實驗結果如下:
這裡寫圖片描述