第1課:引導程式
聲明:轉載請保留:
譯者:http://www.cppblog.com/jinglexy(新的部落格位址是:http://blog.csdn.net/jinglexy)
原作者:xiaoming.mo at skelix dot org
MSN & Email: jinglexy at yahoo dot com dot cn
目标:使"system"從軟碟啟動,并列印"Hello World!" 下載下傳源程式
記憶體尋址
處理器以‘位元組’管理和通路記憶體,每個位元組都有獨立的位址,即實體位址。有兩種位址映射方式:分段和分頁,skelix核心中都用到了。
段對于我們來說再熟悉不過了,先回顧一下dos時期的段吧。它是一個16位的寄存器,是以最多可以直接通路2^16位元組的記憶體,即64K。這對應用程式來說太少了,于是Intel使用Segment:Offset結合方式來表示一個虛拟位址。段寄存器左移4位加上偏移就得到實際的實體位址了。例如,0x 7c 00:0x0189表示實體位址0x 7c 189,而不是0x 7c 000189。計算過程如下:
7C 000
+ 0189
-------
7C 189
現在我們來計算最大可以通路的位址:FFFF:FFFF
FFFF0
+ FFFF
-------
10FFEF
這個範圍是 1M + 65519 bytes, 因為在80386中使用了20位位址線,是以可以額外多通路65519個位元組虛拟位址,例如位址0x100010被映射到位址0x10,通路這兩個位址是等價的。
表示同一個實體位址有多種方式,例如 07C 0:0000和0000: 7C 00 就是一樣的。
另一個概念是線性位址,這個是32位位址,隻有當分頁機制開啟時才有效,文章後面會提到它。
引導過程
當系統上電或RESET時,處理器将執行一些列的初始化,寄存器被設定成非預知狀态,并且cpu處于實模式。也許你想知道cpu是怎樣設定segment:offset為實體位址FFFF0的(0xf000:0xfff0就是bios入口位址),這是因為cs寄存器有一個非可見部分,它儲存了ffff:0000位址,并且cs在初始化時會被裝入f000值。此後以正常方式使用它。當bois取得控制權後,根據使用者配置(從軟驅,硬碟,或cdrom)中讀取第一個sector到 00007C 00,并跳轉到該位址執行(就是引導程式bootstrap)。在bootstrap中我們可以使用bios中斷,但是進入kernel後就不能再使用了。
程式一:使用as和ld的範例
你可以在下載下傳源程式的01/first.cry/bootsect.s
.text .text表示代碼段
.globl start表示start可以用作外部符号
.code16 GCC預設使用32位位址和操作數,這裡告訴它使用16位
start:
jmp start 死循環
.org 0x1fe, 0x90 .org NEW-LC, FILL,說明:這裡填充0x90,是nop指令的機器碼
.word 0xaa55
講解:.org指令訓示下一個資料位址,為了編譯這個程式,我們寫了一個Makefile,總不能老是敲指令吧,呵呵。
網絡上可以找到很多寫Makefile的資料,編譯選項才是我們關注的焦點。
01/first.cry/Makefile
AS=as gcc彙編工具
LD=ld gcc連接配接器
.s.o:
${AS} -a $< -o $*.o >$*.map
all: final.img
final.img: bootsect
mv bootsect final.img
bootsect: bootsect.o
${LD} --oformat binary -N -e start -Ttext 0x 7c 00 -o bootsect $<
講解:ld可以被配置為支援多于一種的目标檔案. binary表示沒有程式頭和其他資訊,僅僅是一些裸資料。如果沒有這個選項,将被預設連結為elf格式。-N把text和data節設定為可讀寫。-Ttext将text節起始位址設定為0x 7c 00(在jmp和資料引用等重定位連結時會用到這個參考值),所有的引用位址都是在 7c 00這個位址上加出來的。-e選項指定程式入口點
現在我們運作make指令編譯一下:
[[email protected]~/source/os/skelix/01/first.cry]$ ls
bootsect.s COPYING Makefile
[[email protected]~/source/os/skelix/01/first.cry]$ make
as -a bootsect.s -o bootsect.o >bootsect.map
ld --oformat binary -N -e start -Ttext 0x 7c 00 -o bootsect bootsect.o
mv bootsect final.img
[[email protected]~/source/os/skelix/01/first.cry]$ ls
bootsect.map bootsect.o bootsect.s COPYING final.img Makefile
[[email protected]~/source/os/skelix/01/first.cry]$
現在,我們啟動vmware,運作,載入軟驅映象檔案"final.img",我們得到一個黑屏,這是正确的,因為我們什麼也沒有做。
程式一:顯示 Hello World!
好了,上面的黑屏程式并不是太好玩,現在我們嘗試在上面列印"Hello World!"
01/hello.world/bootsect.s
.text
.globl start
.code16
start:
jmp code
msg: 使用jmp指令跳過該變量,這是我們為什麼在Makefile使用-N連結選項了
.string "Hello World!/x0"
code:
movw $0xb800,%ax
movw %ax, %es es段設定成B800,如前所述,segment:offset位址映射方式,它指向B8000,
這意味着第一個位元組位址是0(映射到B8000),屬性位元組是1(映射到B8001)
B8001值設定為0x07可以将這個byte顔色設定為黑底白字。
xorw %ax, %ax
movw %ax, %ds
movw $msg, %si 為movsb指令設定正确的si和di
xorw %di, %di
cld
movb $0x07, %al 字的顔色
1:
cmp $0, (%si)
je 1f
movsb
stosb
jmp 1b
1: jmp 1b
.org 0x1fe, 0x90
.word 0xaa55