呵呵,終于將linux 0.11 下面的boot文件夾下的三個(gè)文件讀完,下面是相關(guān)注釋,沒有匯編基礎(chǔ)的人也是可以讀的。廢話少說,下面就是linux的源碼了。
參考資料 Linux內(nèi)核完全注釋.pdf
網(wǎng)上相關(guān)資料
!時(shí)間 : 2010-1-14
!工作 : 閱讀linux 0.11 源碼中的bootsect.s
!總體linux啟動(dòng)過程如下:
!
!當(dāng)PC得電源打開之后,80x86結(jié)構(gòu)的CPU將自動(dòng)進(jìn)入實(shí)時(shí)模式,并且從0xFFFF0開始自動(dòng)執(zhí)行程序代碼,這個(gè)地址通常是
!ROM-BIOS的地址。PC機(jī)的BIOS將執(zhí)行系統(tǒng)的檢測(cè),并且在物理地址的0處開始初始化中斷向量。此后,它將可啟動(dòng)設(shè)備的第一
!扇區(qū)(512字節(jié))讀入內(nèi)存的絕對(duì)地址0x7c00處,并且跳轉(zhuǎn)到這個(gè)地方。啟動(dòng)設(shè)備通常是軟盤或者是硬盤。這里的敘述是很簡(jiǎn)單
!的,但是這已經(jīng)足夠理解內(nèi)核的初始化的工作過程。
!
!linux的0x9000由BIOS讀入到內(nèi)存的絕對(duì)地址0x7c00(31k)處,當(dāng)它被
!執(zhí)行時(shí)就會(huì)把自己移動(dòng)到絕對(duì)地址0x90000處,并把啟動(dòng)設(shè)備中后2kb字節(jié)代碼(boot/setup.s)讀入到內(nèi)存0x90200處,而內(nèi)核的
!其他部分則被讀入到從地址0x10000的開始處。在系統(tǒng)的加載期間顯示信息?Loading...",然后將控制權(quán)傳遞給boot/setup.s中
!的代碼.這是另一個(gè)實(shí)時(shí)模式匯編程序。
!
!系統(tǒng)啟動(dòng)部分識(shí)別主機(jī)的某些特性以及vga卡的類型。如果需要,它會(huì)要求用戶為控制臺(tái)選擇顯示模式。然后整個(gè)系統(tǒng)從地址
!0x10000移至0x0000處,進(jìn)入保護(hù)模式病跳轉(zhuǎn)至系統(tǒng)的余下部分。此時(shí)所有的32位運(yùn)行方式的設(shè)置啟動(dòng)被完成:idt,gdt,ldt被
!加載,處理器和協(xié)處理器也確認(rèn),分頁(yè)的工作也設(shè)置好了。最終將調(diào)用init/main.c中的main程序。上述的操作的源代碼是在
!boot/head.s中的。這可能是整個(gè)內(nèi)核中最有訣竅的代碼了。注意如果在上述任何一步中出現(xiàn)了一步錯(cuò)誤。計(jì)算機(jī)就會(huì)死鎖。在
!操作系統(tǒng)還沒有完全運(yùn)轉(zhuǎn)之前是處理不了錯(cuò)誤的。
!
!
!bootsec.s文件說明如下:
!bootsec.s代碼是磁盤的引導(dǎo)塊程序,駐留在磁盤的第一扇區(qū)。在PC機(jī)加電rom bios自檢之后,引導(dǎo)扇區(qū)由bios加載到內(nèi)存0x7c00
!處,然后將自己移動(dòng)到內(nèi)存0x90000處。該程序的主要作用是首先將setup模塊從磁盤加載到內(nèi)存中,緊接著bootsect的后面位置
!(0x90200),然后利用bios中斷0x13中斷去磁盤參數(shù)表中當(dāng)前引導(dǎo)盤的參數(shù),然后在屏幕上顯示"Loading system..."字符串。再者
!將system模塊從磁盤上加載到內(nèi)存0x10000開始的地方。隨后確定根文件系統(tǒng)的設(shè)備號(hào),如果沒有指定,則根據(jù)所保存的引導(dǎo)盤的每
!類型和種類,并保存設(shè)備號(hào)與boot_dev,最后長(zhǎng)跳轉(zhuǎn)到 setup程序開始處0x90200執(zhí)行setup程序。
!
!
!注釋如下:
!
! SYS_SIZE is the number of clicks (16 bytes) to be loaded.
! 0x3000 is 0x30000 bytes = 196kB, more than enough for current
! versions of linux
!
SYSSIZE = 0x3000
!
!以下是這一段代碼的翻譯。
! bootsect.s
!bootsect.s被bios啟動(dòng)程序加載至0x7c00 31k處,并將自己移動(dòng)到地址0x90000 576k處,并跳轉(zhuǎn)到那里。
!
!它然后利用bios中斷將setup直接加載到自己后面0x90200 576.5k,并將system加載到地址0x10000處。
!
!注意 : 目前的內(nèi)核系統(tǒng)最大的長(zhǎng)度限制為8*65536 512k字節(jié),即使是在將來這也應(yīng)該沒有問題的。我想讓他保持簡(jiǎn)單明了,
!這樣512k的最大內(nèi)核長(zhǎng)度應(yīng)該足夠了,尤其是這里沒有向minix中一樣包含緩沖區(qū)高速緩沖。
!
!加載程序已經(jīng)做的足夠簡(jiǎn)單了,所以持續(xù)的獨(dú)處錯(cuò)誤將導(dǎo)致死循環(huán)。只能手工重啟。只要可能,通過一次取出所有的扇區(qū),加載的
!過程可以做的很快.
!
!
!
! bootsect.s (C) 1991 Linus Torvalds
!
! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
! iself out of the way to address 0x90000, and jumps there.
!
! It then loads 'setup' directly after itself (0x90200) 256b, and the system
! at 0x10000, using BIOS interrupts.
!
! NOTE! currently system is at most 8*65536 bytes long. This should be no
! problem, even in the future. I want to keep it simple. This 512 kB
! kernel size should be enough, especially as this doesn't contain the
! buffer cache as in minix
!
! The loader has been made as simple as possible, and continuos
! read errors will result in a unbreakable loop. Reboot by hand. It
! loads pretty fast by getting whole sectors at a time whenever possible.
!六個(gè)全局標(biāo)示符
.globl begtext, begdata, begbss, endtext, enddata, endbss
!文本段
.text
begtext:
!數(shù)據(jù)段
.data
begdata:
!堆棧段
.bss
begbss:
!文本段
.text
SETUPLEN = 4 ! nr of setup-sectors setup程序的扇區(qū)數(shù)(setup-sectors)值
BOOTSEG = 0x07c0 ! original address of boot-sector bootsect的原始地址
INITSEG = 0x9000 ! we move boot here - out of the way 將bootsect移動(dòng)到這里
SETUPSEG = 0x9020 ! setup starts here setup程序開始地址
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536). 將system模塊加載到的地址
ENDSEG = SYSSEG + SYSSIZE ! where to stop loading 停止加載的地址
! ROOT_DEV: 0x000 - same type of floppy as boot.
! 0x301 - first partition on first drive etc
ROOT_DEV = 0x306 !根文件系統(tǒng)設(shè)備是第二硬盤的第一個(gè)分區(qū)
!0x300 -- /dev/hd0
!0x301 -- /dev/hd1
!...
!0x309 -- /dev/hd9
entry start 告知連接程序,程序充start標(biāo)號(hào)開始。
start:
////////////////////////////////////////////////////////////////////////////////////////////////////////////
!下面的代碼是將自身bootsect從目前位置0x07c0 31k移動(dòng)到0x9000 576k處,然后跳轉(zhuǎn)到本程序的下一條語句處。
!
!此時(shí)(在實(shí)時(shí)模式下)內(nèi)存使用如下的分布 :
!0 0x7c00(bootsect.s)
!--------++++++++++---------------------------
!ds = 0x7c00
! <-
mov ax,#BOOTSEG
mov ds,ax
!es = 0x9000
mov ax,#INITSEG
mov es,ax
!移動(dòng)計(jì)數(shù)的值 256
!啟動(dòng)代碼是512kb
mov cx,#256
!源地址 ds : si = 0x07co : 0x0000
sub si,si
!目的地址 es : di = 0x9000 : 0x0000
sub di,di
!重復(fù)執(zhí)行知道cx = 0。循環(huán)程序?qū)崿F(xiàn)的另一種方法是利用串操作處理的重復(fù)指令rep。rep指令以cx為重復(fù)次數(shù),
!當(dāng)指令被重復(fù)執(zhí)行完一次,那么cx的值會(huì)自動(dòng)減一。rep指令和串操作指令movs,stos配合使用,它將這兩條指令
!重復(fù)執(zhí)行cx次。
rep
!移動(dòng)一個(gè)字
movw
!此時(shí)的內(nèi)存使用情況如下 :
!0 0x7c00 0x9000
!----------+++++++++---------+++++++++---------
!兩個(gè)使用中的內(nèi)存是相同的
!在匯編中的段的使用
!
!間接跳轉(zhuǎn)。這里INITSEG指出跳轉(zhuǎn)到的地址。
!其格式為:jmpi offset(標(biāo)號(hào)), segment selector
jmpi go,INITSEG -- 0x9000
//////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////
!在將自己成功移動(dòng)之后,下面的幾步是為下面加載setup程序做準(zhǔn)備。
!
!在執(zhí)行jmpi指令時(shí),cs段會(huì)被自動(dòng)更新。注意的是cs寄存器在call或者是jmp指令時(shí)會(huì)自動(dòng)更新。將 ds es ss都設(shè)置成代碼所
!在的段。
go: mov ax,cs
mov ds,ax
mov es,ax
!將堆棧指針sp指向0x9ff00 (0x9000 : 0xff00)
! put stack at 0x9ff00.
!SS被成為堆棧段寄存器,用于存放堆棧段的基值.
mov ss,ax
!代碼段的移動(dòng),需要重新設(shè)置堆棧段的位置。sp只要指向遠(yuǎn)大于512便宜處都可以。
mov sp,#0xFF00 ! arbitrary value >>512
!//////////////////////////////////////////////////////////////////////////////////////////////////////////
!在bootsect程序塊后緊隨著加載setup模塊。注意es已經(jīng)設(shè)置好了。es是指附加段寄存器。附加段寄存器是es,它的作用是很大的.
!因?yàn)槲覀冊(cè)谔幚頂?shù)據(jù)的時(shí)候,往往需要用到兩個(gè)數(shù)據(jù)段,特別是在字符串的處理方面,使用兩個(gè)數(shù)據(jù)段簡(jiǎn)便了許多的操作.
!
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.
!
!下面的這段代碼的主要作用是使用int 0x13把磁盤上的setup模塊加載到內(nèi)存中,位置在bootsect.s (0x90000 + 512字節(jié))之后,
!真?zhèn)€過程主要是操作寄存器ax,bx,cx,dx等四個(gè)寄存器。
!
!
!///////////////////////////////////////////////////////////////////////////////////////////////////////
!設(shè)置load_setup標(biāo)號(hào),是為了執(zhí)行j load_setup語句。
load_setup:
mov dx,#0x0000 ! drive 0, head 0
mov cx,#0x0002 ! sector 2, track 0
mov bx,#0x0200 ! address = 512, in INITSEG
mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors
int 0x13 ! read it 調(diào)用中斷信號(hào),開始讀取。
!進(jìn)位操作標(biāo)示符cf = 0表示操作成功。
jnc ok_load_setup ! ok - continue·如果成功就跳轉(zhuǎn)到下面的ok_load_setup
!否則執(zhí)行下面的代碼,復(fù)位磁盤再次執(zhí)行這段代碼
mov dx,#0x0000
mov ax,#0x0000 ! reset the diskette 磁盤復(fù)位
int 0x13 !ld86中就有j這條指令,等價(jià)于jmp。這條語句的含義是 :
!跳轉(zhuǎn)回去繼續(xù)執(zhí)行,如果總是失敗系統(tǒng),將總執(zhí)行這段代碼
j load_setup
!//////////////////////////////////////////////////////////////////////////////////////////////////////
!時(shí)間 : 2010-1-15
!工作量 : 繼續(xù)閱讀linux 0.11 源碼
!時(shí)間 : 2010-1-17
!工作量 : 繼續(xù)閱讀linux源碼。
!
!如果上面講setup模塊順利讀入到內(nèi)存中,那么執(zhí)行下面的ok_load_setup代碼。
!
ok_load_setup:
! Get disk drive parameters, specifically nr of sectors/track
!取得磁盤驅(qū)動(dòng)器的參數(shù),特別是沒道的扇區(qū)數(shù)量。
!取得磁盤的參數(shù)使用的是int 0x13中斷來實(shí)現(xiàn),其調(diào)用格式如下 :
!ah = 0x08 dl = 啟動(dòng)器號(hào)
!返回信息如下 :
!如果出錯(cuò)的話cf置位,并且ah = 狀態(tài)碼
!ah = 0, al = 0 bl = 驅(qū)動(dòng)器類型(at/ps2)
!ch = 最大磁盤號(hào)的低8位, cl = 每磁盤的最大扇區(qū)數(shù)(位0-5),最大磁道號(hào)高2位(位6-7)
!dh = 最大磁道數(shù) dl = 驅(qū)動(dòng)器數(shù)量
!es : di = 軟驅(qū)磁盤參數(shù)表
!
!調(diào)用中斷0x13
mov dl,#0x00 !清空dl,以獲得驅(qū)動(dòng)器號(hào)
mov ax,#0x0800 ! AH=8 is get drive parameters
int 0x13
mov ch,#0x00
///////////////////////////////////////////////////////////
!先講一下寄存器的默認(rèn)組合問題,比如指令mov [si], ax表示將ax中的內(nèi)容存入ds:si指向的內(nèi)存單元,也就是說在寄存器間
!接尋址的情況下,以si間接尋址時(shí)總是默認(rèn)以ds為相應(yīng)的段地址寄存器。同樣di是以es為默認(rèn)的段地址寄存器。
!第二個(gè)要了解的是“段超越”的問題,就是在某些時(shí)候你不想使用默認(rèn)的段地址寄存器,那
!么你可以強(qiáng)制指定一個(gè)段地址寄存器(當(dāng)然這種強(qiáng)制是在允許的情況下,建議看一下匯編
!教材上的說明),同上例mov [si],ax表示存入ds:si中,但如果你想存入cs指向的段中可
!以這樣mov cs:[si],ax, 這樣就強(qiáng)制指定將ax中的內(nèi)容存入cs:si的內(nèi)存單元。
!第三個(gè)要明白的是seg cs這樣的語句只影響到它下一條指令,比如在linux啟動(dòng)代碼中的一段:
!seg cs
!mov sectors,ax
!mov ax,#INITSEG
!要說明兩點(diǎn):
!第一,seg cs 只影響到mov sectors,ax而不影響mov ax,#INITSEG
!第二,如果以Masm語法寫,seg cs和mov sectors,ax兩句合起來等
! 價(jià)于mov cs:[sectors],ax,這里使用了間接尋址方式。
! 重復(fù)一下前面的解釋,mov [sectors],ax表示將ax中的內(nèi)容
! 存入ds:sectors內(nèi)存單元,而mov cs:[sectors],ax強(qiáng)制以
! cs作為段地址寄存器,因此是將ax的內(nèi)容存入cs:sectors內(nèi)存
! 單元,一般來說cs與ds的值是不同的,如果cs和ds的值一樣,
! 那兩條指令的運(yùn)行結(jié)果會(huì)是一樣的。(編譯后的指令后者比前
! 者一般長(zhǎng)一個(gè)字節(jié),多了一個(gè)前綴。)
!結(jié)論,seg cs只是表明緊跟它的下一條語句將使用段超越,因?yàn)樵诰?br> ! 譯后的代碼中可以清楚的看出段超越本質(zhì)上就是加了一個(gè)字節(jié)
! 的指令前綴,因此as86把它單獨(dú)作為一條指令來寫也是合理的。
!
!mov cs:[sectors],ax
!
!下面的代碼在linux 2.6.x的內(nèi)核中可能改變。沒有查證。網(wǎng)上有關(guān)于其的討論,認(rèn)為其中含有錯(cuò)誤。
seg cs
mov sectors,cx !sectors在下面定義,保存每個(gè)磁道扇區(qū)數(shù)
///////////////////////////////////////////////////////////
mov ax,#INITSEG
mov es,ax !INITSEG = 0x90000 恢復(fù)es值
! Print some inane message 在顯示一些信息("Loading system...\n"回車換行。共24個(gè)字符)
!//////////////////////////////////////////////////
!讀取光標(biāo)位置
mov ah,#0x03 ! read cursor pos
xor bh,bh ! bh = 0,使用xor指令將bh清0,但是速度比賦值快
int 0x10
!//////////////////////////////////////////////////
!//////////////////////////////////////////////////
!利用int 0x10來實(shí)現(xiàn)將移動(dòng)光標(biāo),并寫字符創(chuàng)
mov cx,#24 ! 共24個(gè)字符
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1 ! msgl下面定義,指向要顯示的字符串
mov ax,#0x1301 ! write string, move cursor
int 0x10 !寫字符串并且移動(dòng)光標(biāo)
!//////////////////////////////////////////////////
! ok, we've written the message, now
! we want to load the system (at 0x10000)
! 現(xiàn)在開始將system模塊加載到內(nèi)存0x10000(64k)處。
!/////////////////////////////////////////////////
mov ax,#SYSSEG
mov es,ax ! segment of 0x010000,現(xiàn)在es就是存放system的段地址
!/////////////////////////////////////////////////
!//////////////////////////////////////////////////
! 調(diào)用邋read_it來實(shí)現(xiàn)讀取磁盤上的system模塊,其中es為輸入?yún)?shù)。
call read_it
!//////////////////////////////////////////////////
!關(guān)閉驅(qū)動(dòng)器馬達(dá),這樣就知道驅(qū)動(dòng)器的狀態(tài)了。
call kill_motor
!/////////////////////////////////////////////////
! After that we check which root-device to use. If the device is
! defined (!= 0), nothing is done and the given device is used.
! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
! on the number of sectors that the BIOS reports currently.
! 此后,我們檢查使用哪個(gè)根文件系統(tǒng)設(shè)備。如果已經(jīng)指定了設(shè)備,就直接使用給定的設(shè)備,
! 否則就需要根據(jù)bios的報(bào)告的每磁道扇區(qū)數(shù)來確定到底是使用/dev/ps0還是/dev/at0
! 上面一行中的兩個(gè)設(shè)備文件的含義 :
! 在linux中軟驅(qū)的主設(shè)備號(hào)是2,次設(shè)備號(hào) = type * 4 + nr,其中nr 為0-3分別
! 對(duì)應(yīng)軟驅(qū)的abcd;type是軟驅(qū)的類型(2->1.2m 7->1.44m等)。因?yàn)?*4+0=28,所
! 以/dev/ps0指的是1.44m a驅(qū)動(dòng)器,起設(shè)備號(hào)是0x021c,同理/dev/at0(2,8)指的是1.2
! m a的驅(qū)動(dòng)器,其設(shè)備號(hào)是0x0208
seg cs
mov ax,root_dev ! 根設(shè)備號(hào)
!////////////////////////////////////////////////////
! 使用cmp和jne指令來實(shí)現(xiàn)條件轉(zhuǎn)移。在匯編語句中的跳轉(zhuǎn)指令分為有條件跳轉(zhuǎn)和
! 無條件跳轉(zhuǎn),jne可解釋為如下:jump not equal
!
cmp ax,#0
jne root_defined
!////////////////////////////////////////////////////
seg cs
mov bx,sectors ! 取上面保存的sectors。如果sectors=15,則說明是
! 1.2mb的驅(qū)動(dòng)器;如果sectors=18,則說明是1.44mb
! 軟驅(qū)。因?yàn)槭强梢龑?dǎo)的驅(qū)動(dòng)器,所以肯定是a驅(qū)。
!////////////////////////////////////////////////////
! 如果跳轉(zhuǎn)到root_defined,則使用ax作為參數(shù)來傳遞。
mov ax,#0x0208 ! /dev/ps0 - 1.2Mb
cmp bx,#15 ! 判斷每磁道扇區(qū)數(shù)是否是15
je root_defined ! 如果等于,則ax中就是引導(dǎo)驅(qū)動(dòng)器的設(shè)備號(hào)
!///////////////////////////////////////////////////
!//////////////////////////////////////////////////////
! 如果跳轉(zhuǎn)到root_defined,使用ax來作為參數(shù)傳遞。
mov ax,#0x021c ! /dev/PS0 - 1.44Mb
!/////////////////////////////////////////////////////
! 使用cmp和je指令來實(shí)現(xiàn),助記符je的含義是jump equal
cmp bx,#18
je root_defined
!/////////////////////////////////////////////////////
////////////////////////////////////////////////////////
undef_root: ! 死循環(huán) - 死機(jī)
jmp undef_root
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
root_defined:
seg cs
mov root_dev,ax ! ax = 0x0208,將檢查的設(shè)備號(hào)保存。
///////////////////////////////////////////////////////
! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:
! 到此,所有的程序都加在完畢,我們就跳轉(zhuǎn)到被加載在bootsect后面的setup程序去。
!
jmpi 0,SETUPSEG ! 跳轉(zhuǎn)到0x9020 : 0000(setup.s的開始處)。
! 本程序到此結(jié)束。呵呵終于結(jié)束了。
! 下面是兩個(gè)子程序。
! This routine loads the system at address 0x10000, making sure
! no 64kB boundaries are crossed. We try to load it as fast as
! possible, loading whole tracks whenever we can.
!
! in: es - starting address segment (normally 0x1000)
!
! 該子程序?qū)⑾到y(tǒng)模塊加載到內(nèi)存地址0x10000處,并確定沒有跨越64kb內(nèi)存邊界,我們?cè)噲D盡快的
! 進(jìn)行加載,只要可能,就每次加載整條磁道的數(shù)據(jù)。
! 輸入es : -- 開始內(nèi)存地址段值 (通常是0x1000)
!
sread: .word 1+SETUPLEN ! sectors read of current track
! 定義變量sread,表示當(dāng)前磁道中以讀的扇區(qū)數(shù)。開始時(shí)已經(jīng)讀
! 進(jìn)1扇區(qū)的引導(dǎo)扇區(qū),bootsect和setup程序所占的扇區(qū)數(shù)SETUPLEN.
!
head: .word 0 ! current head,當(dāng)前磁頭號(hào)。
track: .word 0 ! current track,當(dāng)前磁道號(hào)。
read_it:
! 測(cè)試輸入的段值。必須位于內(nèi)存地址64kb邊界處,否則進(jìn)入死循環(huán)。清bx寄存器,用于表示當(dāng)前段內(nèi)
! 存放數(shù)據(jù)的開始位置。
!
! es = 0x1000
mov ax,es
!//////////////////////////////////////////////
! test指令,實(shí)現(xiàn)將原操作數(shù)用于和目的操作數(shù)按位"與"運(yùn)算,但是結(jié)果并不放在目的地址。
! test指令會(huì)影響到ZF的標(biāo)志位。如果"與"的結(jié)果為0,那么zf=1。
test ax,#0x0fff
!/////////////////////////////////////////////
die: jne die ! es must be at 64kB boundary
! es的值必須是位于64k地址的邊界,否則進(jìn)入死循環(huán)。
xor bx,bx ! bx is starting address within segment
! bx是段內(nèi)偏移地址。
rp_read:
! 判斷是否已經(jīng)全部讀入數(shù)據(jù)。比較當(dāng)前所讀段是否就是系統(tǒng)數(shù)據(jù)的末端所處的段(endseg),
!如果不是,跳轉(zhuǎn)到下面的ok1_read標(biāo)號(hào)處繼續(xù)讀取數(shù)據(jù)。否則退出子程序返回。
!
!
mov ax,es
cmp ax,#ENDSEG ! have we loaded all yet?
jb ok1_read
ret
ok1_read:
! 計(jì)算和驗(yàn)證當(dāng)前的磁道需要讀取的扇區(qū)數(shù),放在ax寄存器中。
! 根據(jù)當(dāng)前磁道還未讀取的扇區(qū)數(shù)以及段內(nèi)數(shù)據(jù)字節(jié)的開始偏
! 移量,計(jì)算如果全部讀取這些未讀扇區(qū),所讀總字節(jié)數(shù)是否
! 會(huì)超過64kb段長(zhǎng)度限制。若會(huì)超過,則根據(jù)此次最多能讀入
! 的字節(jié)數(shù)(64kb - 段內(nèi)偏移量),反算出此次需要讀取的扇區(qū)數(shù)。
!
seg cs
mov ax,sectors ! 取出每個(gè)磁道扇區(qū)數(shù)
sub ax,sread ! 減去當(dāng)前磁道中已經(jīng)讀取的扇區(qū)數(shù)
mov cx,ax ! cx = ax = 當(dāng)前磁道未讀的扇區(qū)數(shù)
shl cx,#9 ! cx = cx * 512
add cx,bx ! cx = cx + 段內(nèi)當(dāng)前的偏移量
! = 此次讀操作后,段內(nèi)共讀入的字節(jié)數(shù)。
! CF是進(jìn)位標(biāo)志,就是說當(dāng)執(zhí)行一個(gè)加法或減
! 法時(shí),最高位產(chǎn)生進(jìn)位或借位時(shí),CF就為1,否則為0.
! ADD它的功能是將源操作數(shù)與目標(biāo)操作數(shù)相加,
! 結(jié)果保存在目標(biāo)操作數(shù)中,并根據(jù)結(jié)果置標(biāo)志位.
! 例如:MOV?。模蹋保玻?!將12H放到數(shù)據(jù)寄存器低8位中,即DL
! add dl,34H 將34H和12H相加,結(jié)果保存在DL中,運(yùn)行后DL為46H
! 他們相加后最高位沒有進(jìn)位,所以CF=0,它們是這樣相加的
! 12H對(duì)應(yīng)的二進(jìn)制 00010010
! 34H對(duì)應(yīng)的二進(jìn)制?。。埃埃保保埃保埃?br> ! ?。?br> ! 01000110
!最高位是第8位,0+0沒有進(jìn)位,所以CF=0
jnc ok2_read ! 如果沒有超過64kb字節(jié),則跳轉(zhuǎn)到ok2_read
! 進(jìn)位時(shí)轉(zhuǎn)移 jnc, cf = 0時(shí)跳轉(zhuǎn)。
je ok2_read !
xor ax,ax ! 若加上此次將讀磁道上所未讀扇區(qū)時(shí)會(huì)超過64kb
sub ax,bx ! 那么計(jì)算此時(shí)最多能讀入的字節(jié)數(shù)64kb - 段內(nèi)讀偏移量
shr ax,#9 ! 再轉(zhuǎn)換成需要讀取的扇區(qū)數(shù)。
ok2_read:
call read_track
mov cx,ax ! cx = 該次操作已讀取的扇區(qū)數(shù)
add ax,sread ! 當(dāng)前磁道上已經(jīng)讀取的扇區(qū)數(shù)。
seg cs
cmp ax,sectors ! 如果當(dāng)前磁道上還有扇區(qū)未讀,則跳轉(zhuǎn)到ok3_read
jne ok3_read
!讀該磁道的下一個(gè)磁頭面上的數(shù)據(jù)。如果已經(jīng)完成,則去讀下一個(gè)磁道。
mov ax,#1
sub ax,head ! 判斷當(dāng)前的磁頭號(hào)
jne ok4_read ! 如果是0磁頭,則再去讀1磁頭面上的數(shù)據(jù)。
inc track ! 否則去讀取下一個(gè)磁道。
ok4_read:
mov head,ax ! 保存當(dāng)前磁道已讀扇區(qū)數(shù)
xor ax,ax ! 清空前磁道已讀扇區(qū)數(shù)
ok3_read:
mov sread,ax ! 保存當(dāng)前磁道已讀扇區(qū)數(shù)。
shl cx,#9 ! 上次已讀扇區(qū)數(shù) * 512字節(jié)
add bx,cx ! 調(diào)整當(dāng)前段內(nèi)數(shù)據(jù)開始位置
jnc rp_read ! 如果小于64kb邊界值,則跳轉(zhuǎn)到邋rp_read處,繼續(xù)
! 讀取數(shù)據(jù)。否則調(diào)整當(dāng)前段,為下一個(gè)段數(shù)據(jù)做準(zhǔn)備。
///////////////////////////////////////////////////
! 將段基址調(diào)整為指向下一個(gè)64kb段內(nèi)存。
mov ax,es
add ax,#0x1000
mov es,ax
///////////////////////////////////////////////////
xor bx,bx ! 清段內(nèi)數(shù)據(jù)開始偏移量
//////////////////////////////////////////////////
jmp rp_read
read_track:
! 讀取當(dāng)前磁道上制定開始的扇區(qū)和需要讀取的扇區(qū)數(shù)的數(shù)據(jù)到es:bx處。
! al - 需要讀取的扇區(qū)數(shù)
! es : bx - 緩沖區(qū)的位置
!
!/////////////////////////////////////////////////
! 信息保護(hù)
push ax
push bx
push cx
push dx
!/////////////////////////////////////////////////
!////////////////////////////////////////////////
! 調(diào)用中斷前的準(zhǔn)備工作
mov dx,track ! 當(dāng)前的磁道號(hào)
mov cx,sread ! 去當(dāng)前磁道上已讀的扇區(qū)數(shù)
inc cx ! cl = 開始讀扇區(qū)
mov ch,dl ! ch = 當(dāng)前磁道號(hào)
mov dx,head
mov dh,dl
mov dl,#0
and dx,#0x0100
mov ah,#2
int 0x13
!//////////////////////////////////////////////////
jc bad_rt ! 如果出錯(cuò)的話,跳轉(zhuǎn)到bad_rt
! 否則執(zhí)行下面的代碼,回復(fù)現(xiàn)場(chǎng)
pop dx
pop cx
pop bx
pop ax
ret
! 執(zhí)行驅(qū)動(dòng)器的復(fù)位操作,在跳轉(zhuǎn)到read_track處重試
bad_rt: mov ax,#0
mov dx,#0
int 0x13
! 回復(fù)現(xiàn)場(chǎng)
pop dx
pop cx
pop bx
pop ax
! 重新在讀
jmp read_track
/*
* This procedure turns off the floppy drive motor, so
* that we enter the kernel in a known state, and
* don't have to worry about it later.
*/
! 這個(gè)子程序關(guān)閉軟驅(qū)的馬達(dá),這樣我們進(jìn)入內(nèi)核之后它處于已知的狀態(tài),所以以后也就
! 無需擔(dān)心了。
kill_motor:
push dx
mov dx,#0x3f2 ! 軟驅(qū)控制卡的驅(qū)動(dòng)端口,只寫
mov al,#0 ! 關(guān)閉馬達(dá)
outb ! 將al中的內(nèi)容傳輸?shù)絛x指定的端口上去
pop dx
ret
sectors: ! 存放的是當(dāng)前啟動(dòng)軟盤每磁道的扇區(qū)數(shù)
.word 0
msg1:
.byte 13,10
.ascii "Loading system ..."
.byte 13,10,13,10
.org 508 ! 標(biāo)示下面的語句從地址508開始,所以邋root_dev
! 在啟動(dòng)扇區(qū)的第508開始的2個(gè)字節(jié)中。
root_dev: ! 這里存放的是根文件系統(tǒng)所在的設(shè)備號(hào),
! 在init/main.c中會(huì)使用。
.word ROOT_DEV
boot_flag: ! 硬盤的有效標(biāo)示
.word 0xAA55
.text
endtext:
.data
enddata:
.bss
endbss: