預備知識: 匯編語言、能讀懂NASM語法、了解BIOS 中斷INT0x10, INT 0x16,虛擬機的使用
涉及工具: NASM,一個文本編輯器(我用的是ConText + NASM語法高亮)
前言: 第一次寫教程,不知道能拿多少分。
“怎樣開始寫自己的操作系統”可能是不少有“非分想法”的愛好者來說是一個難題,在我的大學課程《計算機操作系統教程》以教程的形式充分說明了操作系統的原理,這本書會告訴你各個環節的原語;所謂“原理”就指理論上的東西,實際上我們并不知道操作系統是如何工作的。為了不至于讓有這個非分想法的朋友不至于放棄,這篇日志就帶你走進操作系統開發的殿堂。
一、操作系統是如何啟動的?
首先讓我們來看看計算機的啟動過程,這里我摘抄了一些東西:
“首先讓我們來了解一些基本概念。第一個是大家非常熟悉的BIOS(基本輸入輸出系統),BIOS是直接與硬件打交道的底層代碼,它為操作系統提供了控制硬件設備的基本功能。BIOS包括有系統BIOS(即常說的主板BIOS)、顯卡BIOS和其它設備(例如IDE控制器、SCSI卡或網卡等)的BIOS,其中系統BIOS是本文要討論的主角,因為計算機的啟動過程正是在它的控制下進行的。BIOS一般被存放在ROM(只讀存儲芯片)之中,即使在關機或掉電以后,這些代碼也不會消失。
“第二個基本概念是內存的地址,我們的機器中一般安裝有32MB、64MB或128MB內存,這些內存的每一個字節都被賦予了一個地址,以便CPU訪問內存。32MB的地址范圍用十六進制數表示就是0~1FFFFFFH,其中0~FFFFFH的低端1MB內存非常特殊,因為最初的8086處理器能夠訪問的內存最大只有1MB,這1MB的低端640KB被稱為基本內存,而A0000H~BFFFFH要保留給顯示卡的顯存使用,C0000H~FFFFFH則被保留給BIOS使用,其中系統BIOS一般占用了最后的64KB或更多一點的空間,顯卡BIOS一般在C0000H~C7FFFH處,IDE控制器的BIOS在C8000H~CBFFFH處。”
(以上引自:http://article.pchome.net/2003/09/25/13022.htm)
二、進一步了解引導的關鍵所在
計算機啟動的第一個過程由BIOS完成,它做了什么工作是不我們不必理會的,我們也管不了那它;關鍵在于:BIOS完成設備檢查、初始化、更新之后,根據預先設定的引導設備順序檢查可以引導的設備,比如我們設置啟動順序為1:Floppy(軟驅)2:HDD1(硬盤1) 3:CD-ROM(光驅),那么,BIOS會從軟驅開始查找是否可以啟動,如果軟驅失敗,則會去找HDD1,...... 那么,BIOS如何知道軟盤或硬盤能不能引導呢?這個把戲的關鍵在于:每個可引導設備都有一個512字節的設備頭,如果這個設備可引導,最后兩個字節必須為0x55(低),0xAA(高),如果BIOS找到這個標志,就會將這512字節(注意:只有512字節)讀到內存的0x0000:0x7C00,然后將控制權轉交給(跳到)0x0000:0x7C00的程序,這就意味著:如果我們將軟盤的第一個扇區最后兩個字節打上"0x55,0xAA"的標志,這個軟盤就可以啟動了!當然,BIOS不會知道你這個引導程序是不是有效的,它只認0x55,0xAA。
好了,我們知道寫自己第一個引導程序的關鍵在于: 1. 一個兩字節(一個字長)的引導標志 0x55,0xAA 2. 引導程序必須占512字節
三、看看實例
代碼用NASM編譯:nasmw -f bin boot.asm -o boot.bin 解釋一下上面的命令:在M$命令提示行下編譯,-f bin 表示編譯為純二進制文件(我們不需要任何文件頭),-o boot.bin 編譯。
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; boot.asm ; A demo to show how bootsect works ; Last modified:2005-4-3 10:57:45 ; Copyright (c) 2005,E-mean X. ; ; This program is released under GPL,See document for details ; You can use this code anywhere you want in condition keep autor's info ; original ; ; Author: E-mean X. ; Contact: xemean@sina.com ; Website: http://www.xemean.net/ ; April,02,2005 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; bits 16 ; 偽指令,告訴編譯器這是 16位代碼 org 0x7C00 ; 偽指令,告訴編譯器這段代碼由0x0:0x7C00開始 ;=========================================================================== ; 程序執行的第一條指令必須是跳轉(如果你想使用FAT12這類文件系統的磁盤) ; 必須占用3字節 ;=========================================================================== jmp SHORT main ; 2 bits,跳轉到主程序執行 nop ; 1 bit ;=========================================================================== ; FAT12 文件系統頭,從NYAOS 借過來的,可以參考相關的文檔以獲得更多細節 ; 這個塊會讓 Winimage 認出編譯后的二進制文件為有效的引導文件 ; 如果不使用這個塊,Winimage將不會將其作為引導程序處理 ; 但我們可以借助其它方法和工具處理,比如DEBUG ;=========================================================================== bsOEM db "ExOS0.01" ; OEM String,任意你喜歡的8字節ASCII碼 bsSectSize dw 512 ; Bytes per sector bsClustSize db 1 ; Sectors per cluster bsRessect dw 1 ; # of reserved sectors bsFatCnt db 2 ; # of fat copies bsRootSize dw 224 ; size of root directory bsTotalSect dw 2880 ; total # of sectors if < 32 meg bsMedia db 0xF0 ; Media Descriptor bsFatSize dw 9 ; Size of each FAT bsTrackSect dw 18 ; Sectors per track bsHeadCnt dw 2 ; number of read-write heads bsHidenSect dd 0 ; number of hidden sectors bsHugeSect dd 0 ; if bsTotalSect is 0 this value is ; the number of sectors bsBootDrv db 0 ; holds drive that the bs came from bsReserv db 0 ; not used for anything bsBootSign db 29h ; boot signature 29h bsVolID dd 0 ; Disk volume ID also used for temp ; sector # / # sectors to load bsVoLabel db "NO NAME " ; Volume Label bsFSType db "FAT12 " ; File System type <- FAT 12文件系統 ;=========================================================================== ; Main start here ;=========================================================================== main: cli ; 關閉可屏蔽中斷,以備我們接下來初始化寄存器的工作 mov ax,cs ; 將代碼段傳給ax,實模式下,代碼段與數據段沒什么分別 mov ds,ax ; 數據段寄存器,實際上都是0 mov es,ax ; 附加段 mov ax,ss ; 堆棧段 mov sp,0x7C00-1 ; 堆棧指針,指向0x7BFF sti ; 基本工作已經完成,開放中斷
; mov si,msgHello ; 使 si指向 "Hello World!"字符串 call printStr ; 調用顯示子程序 mov si,fixLine ; 回車換行 call printStr mov si,msgMore ; 顯示更多信息 call printStr
; Mission complete,take a break .loop: xor ax,ax ; ax 清0 int 0x16 ; 調用int 16h 讀取鍵盤輸入
; 重啟計算機 ; mov ax,0x40 ; mov ds,ax ; xor ax,ax ; mov [0x0072],ax ; 向 0x40:0x72寫0后再跳到_ ; jmp 0xFFFF:0x0 ; 0xFFFF:0x0 可以實現重啟
jmp .loop ; 跳轉到 .loop,什么也不做了
;------- END OF MAIN ----------------
;=========================================================================== ; printStr ; sub function for print a string to screen by INT 10H ; 入口:ds:si = 指向目標字符串 ; 返回:無 ;=========================================================================== printStr: push si ; 保護寄存器 push ax push bx
cld ; 清除進位標志位,這個標志位會影響 si 的遞增方向 mov ah,0x0E ; int 0x10 子功能號,顯示字符,參看相關資料以獲得細節 mov bx,0x0007 ; 頁號0,字符前景色 7,淺灰色,試著改變這個數值 ; 會給你的文字增添色彩 .nextChar: lodsb ; [si] -> al,取一個字節碼 or al,al ; 如果取得的字節是0,則表示字符串結束 jz .OK ; 退出 int 0x10 ; 調用BIOS int 10h 中斷 jmp .nextChar ; 繼續下一個字符,直到遇到0 .OK: pop bx ; 恢復寄存器 pop ax pop si ret ; 返回調用程序 ;------- END OF printStr --------------
; data area
msgHello db 'Hello World!',0 ; 以物理 0結束 msgMore db 'Wow,my FIRST boot sector!',0 fixLine db 13,10,0 ; 回車,換行的ASCII碼
; 引導程序必須為512字節,不用的地方以0填充 times 510-($-$$) db 0 ; $表示程序當前位置,$$表示程序開始位置,由編譯器自動計算 BOOT_SIGN DW 0xAA55 ; 最后兩個字節為引導標志55AA
;--------------------------- End of this programme --------------------------------------
編譯成功之后,會生成一個512字節的boot.bin,用Winimage創建一個新的軟盤,在“Options(選項)”菜單里,"Winimage mode selection ..." 選擇“WinImage Professional mode(專家模式)”(如果不選專家模式,將不能進行引導扇區的編輯),之后,"Image-> Boot Sector Properties"(映像->引導扇區屬性)中,將引導扇區改為你剛才編譯的程序,保存,用Vmware/Qemu/Bochs之類的虛擬機工具試試你的啟動盤吧!
發揮你的想像力,讓你的引導程序實現更多的功能!
-----------------------------------------------------------
E-mean X. April,03,2005
轉載請說明作者及出處 |