?? bootsect.s
字號(hào):
.model tiny
.386p
;// SYSSIZE是要加載的節(jié)數(shù)(16字節(jié)為1節(jié))。3000h共為30000h字節(jié)=192kB
;// 對(duì)當(dāng)前的版本空間已足夠了。
SYSSIZE = 3000h ;// 指編譯連接后system模塊的大小。
;// 這里給出了一個(gè)最大默認(rèn)值。
SETUPLEN = 4 ;// setup程序的扇區(qū)數(shù)(setup-sectors)值
BOOTSEG = 07c0h ;// bootsect的原始地址(是段地址,以下同)
INITSEG = 9000h ;// 將bootsect移到這里
SETUPSEG = 9020h ;// setup程序從這里開始
SYSSEG = 1000h ;// system模塊加載到10000(64kB)處.
ENDSEG = SYSSEG + SYSSIZE ;// 停止加載的段地址
;// DEF_ROOT_DEV: 000h - 根文件系統(tǒng)設(shè)備使用與引導(dǎo)時(shí)同樣的軟驅(qū)設(shè)備.
;// 301 - 根文件系統(tǒng)設(shè)備在第一個(gè)硬盤的第一個(gè)分區(qū)上,等等
ROOT_DEV = 301h;//指定根文件系統(tǒng)設(shè)備是第1個(gè)硬盤的第1個(gè)分區(qū)。這是Linux老式的硬盤命名
;//方式,具體值的含義如下:
;//設(shè)備號(hào) = 主設(shè)備號(hào)*256 + 次設(shè)備號(hào)
;// (也即 dev_no = (major<<8 + minor)
;//(主設(shè)備號(hào):1-內(nèi)存,2-磁盤,3-硬盤,4-ttyx,5-tty,6-并行口,7-非命名管道)
;//300 - /dev/hd0 - 代表整個(gè)第1個(gè)硬盤
;//301 - /dev/hd1 - 第1個(gè)盤的第1個(gè)分區(qū)
;//... ...
;//304 - /dev/hd4 - 第1個(gè)盤的第4個(gè)分區(qū)
;//305 - /dev/hd5 - 代表整個(gè)第2個(gè)硬盤
;//306 - /dev/hd6 - 第2個(gè)盤的第1個(gè)分區(qū)
;//... ...
;//309 - /dev/hd9 - 第1個(gè)盤的第4個(gè)分區(qū)
;/* ************************************************************************
; boot被bios-啟動(dòng)子程序加載至7c00h(31k)處,并將自己移動(dòng)到了
; 地址90000h(576k)處,并跳轉(zhuǎn)至那里。
; 它然后使用BIOS中斷將'setup'直接加載到自己的后面(90200h)(576.5k),
; 并將system加載到地址10000h處。
;
; 注意:目前的內(nèi)核系統(tǒng)最大長(zhǎng)度限制為(8*65536)(512kB)字節(jié),即使是在
; 將來這也應(yīng)該沒有問題的。我想讓它保持簡(jiǎn)單明了。這樣512k的最大內(nèi)核長(zhǎng)度應(yīng)該
; 足夠了,尤其是這里沒有象minix中一樣包含緩沖區(qū)高速緩沖。
;
; 加載程序已經(jīng)做的夠簡(jiǎn)單了,所以持續(xù)的讀出錯(cuò)將導(dǎo)致死循環(huán)。只能手工重啟。
; 只要可能,通過一次取取所有的扇區(qū),加載過程可以做的很快的。
;************************************************************************ */
code segment ;// 程序從_main標(biāo)號(hào)開始執(zhí)行。
assume cs:code
start: ;// 以下10行作用是將自身(bootsect)從目前段位置07c0h(31k)
;// 移動(dòng)到9000h(576k)處,共256字(512字節(jié)),然后跳轉(zhuǎn)到
;// 移動(dòng)后代碼的 go 標(biāo)號(hào)處,也即本程序的下一語句處。
mov ax,BYTE PTR BOOTSEG ;// 將ds段寄存器置為7C0h
mov ds,ax
mov ax,BYTE PTR INITSEG ;// 將es段寄存器置為9000h
mov es,ax
mov cx,256 ;// 移動(dòng)計(jì)數(shù)值 = 256字 = 512 字節(jié)
sub si,si ;// 源地址 ds:si = 07C0h:0000h
sub di,di ;// 目的地址 es:di = 9000h:0000h
rep movsw ;// 重復(fù)執(zhí)行,直到cx = 0;移動(dòng)1個(gè)字
; jmp INITSEG:[go] ;// 間接跳轉(zhuǎn)。這里INITSEG指出跳轉(zhuǎn)到的段地址。
db 0eah ;// 間接跳轉(zhuǎn)指令碼
dw go
dw INITSEG
go: mov ax,cs ;// 將ds、es和ss都置成移動(dòng)后代碼所在的段處(9000h)。
mov ds,ax ;// 由于程序中有堆棧操作(push,pop,call),因此必須設(shè)置堆棧。
mov es,ax
;// put stack at 9ff00. 將堆棧指針sp指向9ff00h(即9000h:0ff00h)處
mov ss,ax
mov sp,0FF00h ;/* 由于代碼段移動(dòng)過了,所以要重新設(shè)置堆棧段的位置。
; sp只要指向遠(yuǎn)大于512偏移(即地址90200h)處
; 都可以。因?yàn)閺?0200h地址開始處還要放置setup程序,
; 而此時(shí)setup程序大約為4個(gè)扇區(qū),因此sp要指向大
; 于(200h + 200h*4 + 堆棧大小)處。 */
;// 在bootsect程序塊后緊跟著加載setup模塊的代碼數(shù)據(jù)。
;// 注意es已經(jīng)設(shè)置好了。(在移動(dòng)代碼時(shí)es已經(jīng)指向目的段地址處9000h)。
load_setup:
;// 以下10行的用途是利用BIOS中斷INT 13h將setup模塊從磁盤第2個(gè)扇區(qū)
;// 開始讀到90200h開始處,共讀4個(gè)扇區(qū)。如果讀出錯(cuò),則復(fù)位驅(qū)動(dòng)器,并
;// 重試,沒有退路。
;// INT 13h 的使用方法如下:
;// ah = 02h - 讀磁盤扇區(qū)到內(nèi)存;al = 需要讀出的扇區(qū)數(shù)量;
;// ch = 磁道(柱面)號(hào)的低8位; cl = 開始扇區(qū)(0-5位),磁道號(hào)高2位(6-7);
;// dh = 磁頭號(hào); dl = 驅(qū)動(dòng)器號(hào)(如果是硬盤則要置為7);
;// es:bx ->指向數(shù)據(jù)緩沖區(qū); 如果出錯(cuò)則CF標(biāo)志置位。
mov dx,0000h ;// drive 0, head 0
mov cx,0002h ;// sector 2, track 0
mov bx,0200h ;// address = 512, in INITSEG
mov ax,0200h+SETUPLEN ;// service 2, nr of sectors
int 13h ;// read it
jnc ok_load_setup ;// ok - continue
mov dx,0000h
mov ax,0000h ;// reset the diskette
int 13h
jmp load_setup
ok_load_setup:
;/* 取磁盤驅(qū)動(dòng)器的參數(shù),特別是每道的扇區(qū)數(shù)量。
; 取磁盤驅(qū)動(dòng)器參數(shù)INT 13h調(diào)用格式和返回信息如下:
; ah = 08h dl = 驅(qū)動(dòng)器號(hào)(如果是硬盤則要置位7為1)。
; 返回信息:
; 如果出錯(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ù), 電力= 驅(qū)動(dòng)器數(shù)量,
; es:di -> 軟驅(qū)磁盤參數(shù)表。 */
mov dl,00h
mov ax,0800h ;// AH=8 is get drive parameters
int 13h
mov ch,00h
;// seg cs ;// 表示下一條語句的操作數(shù)在cs段寄存器所指的段中。
mov cs:sectors,cx ;// 保存每磁道扇區(qū)數(shù)。
mov ax,INITSEG
mov es,ax ;// 因?yàn)樯厦嫒〈疟P參數(shù)中斷改掉了es的值,這里重新改回。
;// Print some inane message 在顯示一些信息('Loading system ... '回車換行,共24個(gè)字符)。
mov ah,03h ;// read cursor pos
xor bh,bh ;// 讀光標(biāo)位置。
int 10h
mov cx,27 ;// 共24個(gè)字符。
mov bx,0007h ;// page 0, attribute 7 (normal)
mov bp,offset msg1 ;// 指向要顯示的字符串。
mov ax,1301h ;// write string, move cursor
int 10h ;// 寫字符串并移動(dòng)光標(biāo)。
;// ok, we've written the message, now
;// we want to load the system (at 10000h) 現(xiàn)在開始將system 模塊加載到10000h(64k)處。
mov ax,SYSSEG
mov es,ax ;// segment of 010000h es = 存放system的段地址。
call read_it ;// 讀磁盤上system模塊,es為輸入?yún)?shù)。
call kill_motor ;// 關(guān)閉驅(qū)動(dòng)器馬達(dá),這樣就可以知道驅(qū)動(dòng)器的狀態(tài)了。
;// 此后,我們檢查要使用哪個(gè)根文件系統(tǒng)設(shè)備(簡(jiǎn)稱根設(shè)備)。如果已經(jīng)指定了設(shè)備(!=0)
;// 就直接使用給定的設(shè)備。否則就需要根據(jù)BIOS報(bào)告的每磁道扇區(qū)數(shù)來
;// 確定到底使用/dev/PS0(2,28)還是/dev/at0(2,8)。
;// 上面一行中兩個(gè)設(shè)備文件的含義:
;// nr為0-3分別對(duì)應(yīng)軟驅(qū)A、B、C或D;type是軟驅(qū)的類型(2->1.2M或7->1.44M等)。
;// 因?yàn)?*4 + 0 = 28,所以/dev/PS0(2,28)指的是1.44M A驅(qū)動(dòng)器,其設(shè)備號(hào)是021c
;// 同理 /dev/at0(2,8)指的是1.2M A驅(qū)動(dòng)器,其設(shè)備號(hào)是0208。
;// seg cs
mov ax,cs:root_dev
cmp ax,0
jne root_defined ;// 如果 ax != 0, 轉(zhuǎn)到root_defined
;// seg cs
mov bx,cs:sectors ;// 取上面保存的每磁道扇區(qū)數(shù)。如果sectors=15
;// 則說明是1.2Mb的驅(qū)動(dòng)器;如果sectors=18,則說明是
;// 1.44Mb軟驅(qū)。因?yàn)槭强梢龑?dǎo)的驅(qū)動(dòng)器,所以肯定是A驅(qū)。
mov ax,0208h ;// /dev/ps0 - 1.2Mb
cmp bx,15 ;// 判斷每磁道扇區(qū)數(shù)是否=15
je root_defined ;// 如果等于,則ax中就是引導(dǎo)驅(qū)動(dòng)器的設(shè)備號(hào)。
mov ax,021ch ;// /dev/PS0 - 1.44Mb
cmp bx,18
je root_defined
undef_root: ;// 如果都不一樣,則死循環(huán)(死機(jī))。
jmp undef_root
root_defined:
;// seg cs
mov cs:root_dev,ax ;// 將檢查過的設(shè)備號(hào)保存起來。
;// 到此,所有程序都加載完畢,我們就跳轉(zhuǎn)到被
;// 加載在bootsect后面的setup程序去。
; jmp SETUPSEG:[0] ;// 跳轉(zhuǎn)到9020:0000(setup程序的開始處)。
db 0eah
dw 0
dw SETUPSEG
;//------------ 本程序到此就結(jié)束了。-------------
;// ******下面是兩個(gè)子程序。*******
;// 該子程序?qū)⑾到y(tǒng)模塊加載到內(nèi)存地址10000h處,并確定沒有跨越64kB的內(nèi)存邊界。
;// 我們?cè)噲D盡快地進(jìn)行加載,只要可能,就每次加載整條磁道的數(shù)據(jù)
;//
;// 輸入:es - 開始內(nèi)存地址段值(通常是1000h)
;//
sread dw 1+SETUPLEN ;// 當(dāng)前磁道中已讀的扇區(qū)數(shù)。開始時(shí)已經(jīng)讀進(jìn)1扇區(qū)的引導(dǎo)扇區(qū)
head dw 0 ;// 當(dāng)前磁頭號(hào)
track dw 0 ;// 當(dāng)前磁道號(hào)
read_it: ;// 測(cè)試輸入的段值。必須位于內(nèi)存地址64KB邊界處,否則進(jìn)入死循環(huán)。
mov ax,es ;// 清bx寄存器,用于表示當(dāng)前段內(nèi)存放數(shù)據(jù)的開始位置。
test ax,0fffh
die:
jne die ;// es值必須位于64KB地址邊界!
xor bx,bx ;// 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? 是否已經(jīng)加載了全部數(shù)據(jù)?
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,cs:sectors ;// 取每磁道扇區(qū)數(shù)。
sub ax,sread ;// 減去當(dāng)前磁道已讀扇區(qū)數(shù)。
mov dx,ax ;// ax = 當(dāng)前磁道未讀扇區(qū)數(shù)。
mov cl,9
shl dx,cl ;// dx = ax * 512 字節(jié)。
add dx,bx ;// cx = cx + 段內(nèi)當(dāng)前偏移值(bx)
;// = 此次讀操作后,段內(nèi)共讀入的字節(jié)數(shù)。
jnc ok2_read ;// 若沒有超過64KB字節(jié),則跳轉(zhuǎn)至ok2_read處執(zhí)行。
je ok2_read
xor ax,ax ;// 若加上此次將讀磁道上所有未讀扇區(qū)時(shí)會(huì)超過64KB,則計(jì)算
sub ax,bx ;// 此時(shí)最多能讀入的字節(jié)數(shù)(64KB - 段內(nèi)讀偏移位置),再轉(zhuǎn)換
shr ax,cl ;// 成需要讀取的扇區(qū)數(shù)。
ok2_read:
call read_track
mov dx,ax ;// dx = 該此操作已讀取的扇區(qū)數(shù)。
add ax,sread ;// 當(dāng)前磁道上已經(jīng)讀取的扇區(qū)數(shù)。
;// seg cs
cmp ax,cs:sectors ;// 如果當(dāng)前磁道上的還有扇區(qū)未讀,則跳轉(zhuǎn)到ok3_read處。
jne ok3_read
;// 讀該磁道的下一磁頭面(1號(hào)磁頭)上的數(shù)據(jù)。如果已經(jīng)完成,則去讀下一磁道。
mov ax,1
sub ax,head ;// 判斷當(dāng)前磁頭號(hào)。
jne ok4_read ;// 如果是0磁頭,則再去讀1磁頭面上的扇區(qū)數(shù)據(jù)
inc track ;// 否則去讀下一磁道。
ok4_read:
mov head,ax ;// 保存當(dāng)前磁頭號(hào)。
xor ax,ax ;// 清當(dāng)前磁道已讀扇區(qū)數(shù)。
ok3_read:
mov sread,ax ;// 保存當(dāng)前磁道已讀扇區(qū)數(shù)。
shl dx,cl ;// 上次已讀扇區(qū)數(shù)*512字節(jié)。
add bx,dx ;// 調(diào)整當(dāng)前段內(nèi)數(shù)據(jù)開始位置。
jnc rp_read ;// 若小于64KB邊界值,則跳轉(zhuǎn)到rp_read處,繼續(xù)讀數(shù)據(jù)。
;// 否則調(diào)整當(dāng)前段,為讀下一段數(shù)據(jù)作準(zhǔn)備。
mov ax,es
add ax,1000h ;// 將段基址調(diào)整為指向下一個(gè)64KB段內(nèi)存。
mov es,ax
xor bx,bx
jmp rp_read
;// 讀當(dāng)前磁道上指定開始扇區(qū)和需讀扇區(qū)數(shù)的數(shù)據(jù)到es:bx開始處。
;// al - 需讀扇區(qū)數(shù); es:bx - 緩沖區(qū)開始位置。
read_track:
push ax
push bx
push cx
push dx
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 ;// 取當(dāng)前磁頭號(hào)。
mov dh,dl ;// dh = 磁頭號(hào)。
mov dl,0 ;// dl = 驅(qū)動(dòng)器號(hào)(為0表示當(dāng)前驅(qū)動(dòng)器)。
and dx,0100h ;// 磁頭號(hào)不大于1
mov ah,2 ;// ah = 2, 讀磁盤扇區(qū)功能號(hào)。
int 13h
jc bad_rt ;// 若出錯(cuò),則跳轉(zhuǎn)至bad_rt。
pop dx
pop cx
pop bx
pop ax
ret
;// 執(zhí)行驅(qū)動(dòng)器復(fù)位操作(磁盤中斷功能號(hào)0),再跳轉(zhuǎn)到read_track處重試。
bad_rt:
mov ax,0
mov dx,0
int 13h
pop dx
pop cx
pop bx
pop ax
jmp read_track
;///*
;//* 這個(gè)子程序用于關(guān)閉軟驅(qū)的馬達(dá),這樣我們進(jìn)入內(nèi)核
;//* 后它處于已知狀態(tài),以后也就無須擔(dān)心它了。
;//*/
kill_motor:
push dx
mov dx,3f2h ;// 軟驅(qū)控制卡的驅(qū)動(dòng)端口,只寫。
mov al,0 ;// A驅(qū)動(dòng)器,關(guān)閉FDC,禁止DMA和中斷請(qǐng)求,關(guān)閉馬達(dá)。
out dx,al ;// 將al中的內(nèi)容輸出到dx指定的端口去。
pop dx
ret
sectors dw 0 ;// 存放當(dāng)前啟動(dòng)軟盤每磁道的扇區(qū)數(shù)。
msg1 db 13,10 ;// 回車、換行的ASCII碼。
db "Loading my system ..." ;// 我加了my,共有27個(gè)字符了
db 13,10,13,10 ;// 共24個(gè)ASCII碼字符。
org 508 ;// 表示下面語句從地址508(1FC)開始,所以root_dev
;// 在啟動(dòng)扇區(qū)的第508開始的2個(gè)字節(jié)中。
root_dev dw ROOT_DEV ;// 這里存放根文件系統(tǒng)所在的設(shè)備號(hào)(init/main.c中會(huì)用)。
boot_flag dw 0AA55h ;// 硬盤有效標(biāo)識(shí)。
code ends
end
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -