這是我 N 年前對 bootsect.S 的解析。
以前的文筆充滿年輕活力啊 XD
好似宋朝晏殊《蝶戀花》之「獨上高樓,望盡天涯路」。
感覺現在程式能力減退很多,
以前一天可以 trace 完上千行 Assembly,
外加轉譯 C++ 虛擬碼,再打上一堆註解,
現在看幾行組語就要想個半天 @@...
畢竟有好幾年時間不怎麼碰程式了.....
注意,本文禁止轉載!
// 注意! 文章中 [] 用於指標取值, 例如 [xx]=9 即 C 語言的 *xx=9 // 此相當於 MASM 的慣例寫法. // 至於一般位址, 是用16進位表示, 如 FFF0 或 0xFFF0 或 FFF0h // 或是合成形式: (segment:offset) // 有時可能會和常數值搞混 (雖然藉由上下文仍可辨認出2者), // 這是因為我找不到表示位址值的好方法, 譬如: #FFF0, *FFF0, // (FFF0), <FFF0>, |FFF0|, $FFF0... 看起來都怪怪的.. //-------------------------------------------------------------------- // 揳子: <POST: Power-On Self Test> // // 開機後, power supply (電源供應器) 會先測試電流和電壓是否正常, // 就像 <星界戰旗> 中 <拉菲爾> 的突擊艦巴斯羅伊魯號要啟動時, // 會先做壓力平衡測試一般, 若沒問題, 就送出 power good 信號給 // CPU 中的 8284 計時晶片(clock generator), 使它不會繼續對 CPU // 發出 reset 信號, 接著, 位於主機板上 BIOS ROM 中的 BIOS 程式 // 會被映射到記憶體的 FEOOO~FFFFF, 開機時, CS 的內容全為 1, // 而 IP 的內容全為 0, 所以 CPU 會從位址 FFFF:0000 開始執行, // 雖然由 FFFF0 到 FFFFF 只有區區 16 Byte (在 Real mode 下), // 但已足夠我們放入一個 jump 指令 (一般是: jmp FE05B), // 讓她跳到實際的 ROM BIOS 起始位址去工作. // // 由於各家 BIOS 廠商的 ROM BIOS 程式, 其功能, 大小與起始位址, // 都不太一樣, 所以我們聰明的祖先, 便設計了一個由統一的進入點 // FFFF0, 跳到不同位址去執行的方式, 讓各家 BIOS 都能相容並存. // // BIOS 程式會依序檢測並初始化主機板上的: CPU, ROM BIOS, CMOS RAM, // 8237(DMA), 8255(鍵盤控制器), 基本的64K RAM, 8259(中斷控制器), // 8253(中斷計時器), Cache 控制器, 然後再依序檢測週邊設備: // CMOS RAM, 6845 (CRT控制器), 64KB以上的RAM (若是暖開機則不測試它) // reset 鍵盤, cassette bus, diskette(軟碟機), 硬碟控制器. // // 檢測週邊的方式是到 UMB (Upper Memory Block, A0000h~FFFFFh) // 中, 找找看是否存在週邊設備的 ROM 起始檢測程式, 若有則執行之. // 例如, 它會到到 C0000~C7800 去尋找顯示卡的 ROM 起始檢測程式, // 執行它, 對顯示卡做初始化等等. 至於, 如何測出裝置或晶片功能 // 是否正常呢? 很簡單, 咱們可以先將一段資料寫進去, 然後再讀出來, // 看看是否相同即可. // // 檢查完畢後, 它會嗶一下, 告訴你電腦硬體很健康, 一切平安. // 就像盲腸手術後一定要'那個'一樣. // 接著將位址 00472 填入 1234h, 表示已完成冷開機程序. // BIOS 程式的最後一步是執行 INT 19, INT 19 俗稱: // 1. bios-startup routine (BIOS啟動常式), // 2. 或是叫 bootstrap routine (靴帶式/鞘式啟動常式), // 3. 或是叫 reboot interrupt (重開機中斷, 供 warm boot 叫用) // // 其作用是將軟碟上的 "第0軌/第0磁頭/第1磁區" 的內容 (也就是 // 底下的 bootsect.s) 讀進 RAM 的 7C00h, 然後再跳到 7C00 去執行. // // 軟碟上的 <第0軌/第0磁頭/第1磁區>, 就是我們常常聽到的 boot // sector (開機磁區), 相當於硬碟上由 fdisk 造出的 MBR (Master // Boot Record, 主開機紀錄), 不過兩者的內容不太一樣, // MBR 啟動程式所處理的雜事會多一些 (廢話..), 但兩者都以 55AA // 作為結束標記. (當然地, 也有不是使用 55AA 的系統) // //-------------------------------------------------------------------- // <bootsect.s> 內容簡介: // // 1. 本程式一開始被 bios-startup 常式載入到位址 7C00h 去執行, // 執行後, 它會將自己複製一份到位址 90000, 然後跳到那兒去執行 // // 2. 將中斷向量 1Eh 所指涉的磁碟參數表複製一份到 9000h:4000h-12 // 將其內紀錄的每軌的磁區數增加到 36, 再將向量 1Eh 指向此新表, // 並呼叫 ah=0,int 13h, 重置磁碟, 使設定能發揮功能. // // 3. 載入位於啟動磁區之後的 setup-sectors (啟動程式) // 呼叫 ah=0,int 13h, 將其載入到 bootsect 之後 (90200h) // // 4. 讀取磁碟裝置參數, 如 sectors 與 track 的數目, // 然後將系統核心載入到 0x10000 // // 5. 選擇一個 root device (這是啥?) //-------------------------------------------------------------------- // 虛擬碼如下: // bootsect.s 將被 bios-startup 常式載入到位址 0x7C00 , // 之後他會將自己複製一份到位址 0x90000, 然後跳到那兒去執行. // 為何不直接從 7C00 開始執行就好呢? 我猜測, 因為有可能咱們 // 選用硬碟開機, 此時 MBR 中紀錄著用來開機的磁區, 位於哪個分 // 割區(partition)中. 於是, 我們將 MBR 的資料放到 7C00 上, // 然後 MBR 再將自己移到 90000 去執行, 它的動作是將設定用來 // 開機的那個磁區, 放到 7C00 上去執行. // 如此, 我們可載入多個磁區資料, 到特定的執行點(7C00)上去執行, // 最後執行到的那個, 便是最正確恰當的開機磁區(boot sector)! // (因此, 只要載入不同的 loader, 便可實現多系統開機) INITSEG = 0x9000 _main: DS = 0x07C0 ES = 0x9000 for( SI=DI=0,CX=256; CX--;) [ES:DI++] = [DS:SI++] //將自己拷貝到 0x9000 jmpi go,INITSEG //跳到 9000h, 位移為 go 的地方開始執行 go: //亦即 CS= 9000h, IP= OFFSET go DI = 0x4000-12 //4000 是我們隨便找的一個大於 bootsect DS = 0x9000 //容量的位址值, 用來暫存磁碟參數表 SS = 0x9000 //將堆疊指向 0x9000:0x4000-12 SP = 0x4000-12 // 做完 POST 後, 中斷向量 1Eh 會指向磁碟參數表, // 由於大多數 BIOS 預設的磁碟參數表, 每軌的磁區數平均是 7 個 // 這將造成編號大於 7 的磁區無法被讀取, 是故, 咱們將磁碟參數 // 表 (共 11 Byte) 先複製一份到 9000h:4000h-12, 然後將參數 // 表第 4 Byte 的內容 (紀錄每軌的磁區數) 調到 36, // 再將中斷向量 1Eh 指向這個新的磁碟參數表 (9000h:4000h-12) FS = DS = 0 //將 FS:BX 指向磁碟參數表 BX = 0x78 //78h = 中斷向量 1Eh 的起始位址 DS:SI = *BX //令 DS:SI = 磁碟參數表的 seg:ofs for( CL=6; CL--;) //從 [0:78h] 拷貝資料到 9000h:4000h-12 [ES:DI++] = [DS:SI++] DI = 0x4000-12 DS = 0x9000 [DI+4] = 36 //設定磁區數 [1Eh] = ES:DI //將 1Eh 號中斷指向 9000:4000-12 // 接著將位於 bootsect 之後的 setup-sectors (啟動程式) // 載到位址 0x90200 load_setup: DL = AH = 0 //重設一下磁碟機 int 13h //讓剛剛的設定能發揮作用 DX = 0 //磁碟機號碼=0, 磁頭號碼=0 CL = 2 //起始磁區號碼=2, 磁軌號碼=0 BX = 200h //將資料讀到 ES:BX (=0x90200) 中 AH = 2 AL = 4 //欲讀取的磁區數 int 13h //讀取磁區資料! // 接著, 取得磁碟裝置參數 (如 sectors 與 track 的數目) // 由於沒有任何 BIOS 中斷能取得磁區數目, 所以底下就用 36, 18, // 15 這 3 個猜測值一一去測試是否能讀取到磁區, 若都失敗就用 9. ok_load_setup: Byte disksizes= {36,18,15,9} //欲測試的磁區數大小 SI = &disksizes for(;AX = [DS:SI++], SI<&disksizes[4];) { CX = AX DX = 0 //drive 0, head 0 ES = 9000h BX = 0A00h AX = 0201h //service 2, 1 sector int 13h //嘗試讀取磁區資料 } print "\nLoading" // 接著將系統核心載入到 0x10000 (以 ES:BX 指向載入時 // 的緩衝區), 並確保載入的資料不會跨越 64kB 的邊界 read_it: //載入系統核心 AX = ES = 0x1000 sread = 5 BX = 0 U2B sread = 0 //目前磁軌上可讀取的磁區數 U2B head = 0 //目前的磁頭編號 U2B track = 0 //目前的磁軌編號 while(1){ #ifdef __BIG_KERNEL__ #define CALL_HIGHLOAD_KLUDGE .word 0x1eff,0x220 CALL_HIGHLOAD_KLUDGE //this is within setup.S #else //但是 setup.S 在哪咧? AX = ES - 1000h #endif if( AX>8000h ) break; CX = sectors - sread //= 尚未讀取的磁區數 CX = CX*512 + BX //= 目前已讀到的段內偏移位置 ES:BX if( CX >= 0x10000) AX = (-BX)>>9 //???? call read_track //傳入 es:bx=緩衝區, AL=欲讀取的磁區數 CX = AX //CX = 剛剛讀入的磁區數 if(AX==sectors) //若已讀完目前磁軌上的磁區 if(head==0) head=1; //若目前位於第 0 磁頭, 則換到第1個 else track++; //已讀完目前磁軌上的磁區, 換下一軌 sread += CX BX += CX<<9 //調整緩衝區偏移指標 if( BX<65536) continue //若 BX < 64K 則繼續重複讀取 ES += 10 //否則就位移到下一個 64K 的開頭 } //並繼續重複讀取 goto kill_motor read_track: print '.' DX = track //DX = 目前的磁軌編號 CX = sread+1 //CX = 欲讀取的起始磁區編號(從1開始算) CH = CL //CH = 磁軌編號 DH = head //DH = 目前的磁頭編號 DL = 0 //DL = 0 (磁碟機編號 A) AH = 2 int 13h ret // 然後關閉磁碟馬達 kill_motor: DX = 0x3f2 AL = 0 OUTB // 最後, 決定要使用哪個 root-device. 若已有指定好的, 就用它. // 否則就依據我們前面所猜測的磁區數目, 從 /dev/fd0H2880 (2,32) // 或 /dev/PS0 (2,28) 或 /dev/at0 (2,8) 中選出一個. if(root_dev==0) //若並未指定 root-device switch(sectors){ case 15: root_dev = 208h; break; csae 18: root_dev = 1ch; break; case 36: root_dev = 20h; break; default: root_dev = 0; } // 至此, 一切都準備好了, 便跳到位於 bootblock 後的 // setup-routine 去執行! jmpi 0,SETUPSEG //------------------------------------------------------------------------ // This is the assembly source for the boot sector used by the // Linux free operating system. If it looks unfamiliar for intel // assembly, that's because it gets run through the standard C // preprocessor (CPP) and has comments starting with '!' stripped off.. ! bootsect.s 將被 bios-startup 常式載入到位址 0x7c00 處, ! 之後他會將自己複製一份到位址 0x90000 處, 然後跳到 0x90200 ! 去執行它的起始(setup)程式. 並呼叫中斷將系統載到 0x10000 去. ! ! 由於目前系統的大小約 (508KB = 8*65536-4096) Bytes. 這樣的大小, ! 即使是在往後的日子裡, 也已經是夠用的了! 且一個 OS 應該盡量維持 ! 簡明易懂才是. 508 kB 絕對夠用, 而且這個 Liunx 核心不像 minix ! 那樣還包含了 buffer cache(快取緩衝區), 再說..她還是被壓縮過的呢. ! ! 這個 loader 已被盡可能的簡化了, 所以啦, 持續的讀取錯誤很容易就 ! 會讓它死當, 然後必須煩勞你手動重開機. 本程式會盡可能一次讀取多 ! 個磁軌, 以加速磁區資料的載入. ;#include <linux/config.h> //for CONFIG_ROOT_RDONLY ;#include <asm/boot.h> .text SIZEBYTES = SIZEDISKB+511 ! round disk.b size for division SETUPSECS = SIZEBYTES/512 ! 啟動磁區(setup sectors)數 (一般= 4) BOOTSEG = 0x07C0 ! 原始的 boot-sector 位址 INITSEG = 0x9000 ! 我們會將啟動程式碼備份到此處 SETUPSEG = INITSEG+0x20 ! 啟動程式從這開始執行 SYSSEG = 0x1000 ! 系統會載入到 0x10000 (65536). SYSSIZE = 0x8000 ! 系統大小: 以 16 Byte 為單位做載入 ASK_VGA = 0xfffd ! ROOT_DEV & SWAP_DEV are now written by "build". ROOT_DEV = 0 SWAP_DEV = 0 #ifndef SVGA_MODE # define SVGA_MODE ASK_VGA #endif #ifndef RAMDISK # define RAMDISK 0 #endif #ifndef CONFIG_ROOT_RDONLY # define CONFIG_ROOT_RDONLY 1 #endif ! 本程序執行時, 如同一般程式, 需要一個程式進入點標示: .globl _main _main: mov ax,#BOOTSEG mov ds,ax ! DS= 0x07C0 mov ax,#INITSEG mov es,ax ! ES= 0x9000 mov cx,#256 ! 總共要移動 512 Bytes sub si,si sub di,di cld rep movsw ! 將自己拷貝到 0x9000 jmpi go,INITSEG ! 跳到 0x9000 ! 目前 ax 和 es 的內容是 INITSEG go: mov di,#0x4000-12 ! 0x4000 是一個大於 bootsect + length ! of setup + room for stack 大小的任 ! 意值, 12 則是磁碟參數表的大小 ! (事實上是11) mov ds,ax ! DS = 0x9000 mov ss,ax ! put stack at INITSEG:0x4000-12. mov sp,di /* 大多數 BIOS 的磁碟參數表 (disk parameter tables) 預設的每軌磁 * 區數太少 (一般平均是 7 個), 以致於程式無法讀取到超過預設最大值 * 之後的磁區, 而且我們的 loader 不可能一次只讀取 1 個磁區, 那樣 * 太慢了! 使用者會因載入時間太久, 而開始幹譙. * 所以我們的 loader 程式, 必須另外建一個磁碟參數表, 並將每軌磁區 * 數提升到 36 (此為 ED 2.88 上所能遇到的最大值). * 此處, 值太大並不是壞事, 太低才是! */ ! 目前 ds=es=ss=cs= INITSEG, fs = 0, gs 未使用, cx = 0 ;;; mov fs,cx ! 用 ;;; 所註解掉的是舊版的碼 mov bx,#0x78 ! 將 fs:bx(指標) 指向 parameter table push ds ;;; seg fs push cx ! contains 0 from rep movsw above pop ds ! lds si,(bx) ! 將 ds:si(變數值) 填入磁碟參數表的所在位址 mov cl,#6 ! copy 12 bytes cld ! push di ! DI= 0x4000-12 rep ! movsw ! 從 [0:78h] 拷貝 12 Byte 到 9000h:4000h-12 pop di ! DI= 0x4000-12 pop ds ! DS= 0x9000 movb 4(di),*36 ! 在磁碟參數表中的相關欄位, 設定磁區數為 36 ;;; seg fs push ds ! 此處將 1Eh 號中斷指向 9000:4000-12 push #0 pop ds mov (bx),di ! [78h] = 0x4000-12 ;;; seg fs mov 2(bx),es ! [7Ah] = 0x9000 pop ds ! 由 bootblock 載入 setup-sectors. ES=0x9020, CX=0. load_setup: xor ah,ah ! 做 I/O 前先重設一下磁碟機 xor dl,dl ! 磁碟機號碼= 0 int 0x13 ! 讓剛剛的設定能發揮作用 ! 讀取磁區資料: xor dx, dx ! 磁碟機號碼(drive)=0, 磁頭號碼(head)=0 mov cl,#0x02 ! 起始磁區號碼(sector)=2, 磁軌號碼(track)=0 mov bx,#0x0200 ! 將資料讀到 ES:BX (=0x90200) 中 mov ah,#0x02 ! mov al,setup_sects ! nr of sectors(欲讀取的磁區數, 一般=4) int 0x13 ! 讀取磁區資料! jnc ok_load_setup ! ok - continue push ax ! dump error code call print_nl mov bp, sp call print_hex pop ax jmp load_setup ! 再試一次 ok_load_setup: ! 取得磁碟裝置參數 (如 sectors 與 track 的數目) #if 0 xor dl,dl mov ah,#0x08 ! AH=8 is get drive parameters int 0x13 xor ch,ch #else ! 因為好像沒有任何 BIOS 中斷能取得磁區數目, 所以底下就用 36, 18, ! 15 這 3 個猜測值一一去測試是否能讀取到磁區, 若都失敗就用 9. mov si,#disksizes ! 欲測試的磁區數大小, 其內容={36,18,15,9} probe_loop: lodsb ! AX = [DS:SI] = [9000h:#disksizes] cbw ! extend to word mov sectors, ax cmp si,#disksizes+4 ! disksizes+4 的內容是 9 jae got_sectors ! if all else fails, try 9 xchg ax, cx ! cx = track and sector xor dx, dx ! drive 0, head 0 xor bl, bl ! mov bh,setup_sects ! BH = 4 inc bh ! ES:BX = 9000:0A00 shl bh,#1 ! address after setup (es = cs) mov ax,#0x0201 ! service 2, 1 sector int 0x13 ! 讀取磁區資料 jc probe_loop ! CF=1 表失敗, 則回頭嘗試其他值 #endif got_sectors: ! Restore es ;;; mov ax,#INITSEG ;;; mov es,ax ! 秀出一些令人安心的訊息 mov ah,#0x03 ! read cursor pos xor bh,bh int 0x10 mov cx,#9 mov bx,#0x0007 ! page 0, attribute 7 (normal) mov bp,#msg1 ! mov ax,#0x1301 ! write string, move cursor int 0x10 ! 印出 "\nLoading" ! 接著將系統載入到 0x10000 mov ax,#SYSSEG mov es,ax ! segment of 0x10000 call read_it ! 載入系統核心 call kill_motor ! 關閉磁碟馬達 call print_nl ! 換行 ! 底下決定要使用哪個 root-device. 若已有指定好的, 就用它. ! 否則就從 /dev/fd0H2880 (2,32) 或 /dev/PS0 (2,28) 或 /dev/at0 (2,8), ! 中選出一個, 挑選的方式是依據我們前面所猜測的磁區數目, 選出最適用的. seg cs mov ax,root_dev or ax,ax jne root_defined ! 若 root_dev!=0 就跳到 root_defiend seg cs mov bx,sectors mov ax,#0x0208 ! /dev/ps0 - 1.2Mb cmp bx,#15 je root_defined mov al,#0x1c ! /dev/PS0 - 1.44Mb cmp bx,#18 je root_defined mov al,#0x20 ! /dev/fd0H2880 - 2.88Mb cmp bx,#36 je root_defined mov al,#0 ! /dev/fd0 - autodetect root_defined: seg cs mov root_dev,ax ! 到此處, 一切都準備的差不多了, 我們便跳到位於 bootblock ! 之後的 setup-routine 去執行 jmpi 0,SETUPSEG ! 跳到 90200h 去執行 ! 下面的程式碼會將系統載到位址 10000h, 並且確保載入的資料不會 ! 跨越 64kB 的邊界, 並盡可能一次讀取多個磁軌, 以加速載入動作. ! ! 輸入參數: es = 起始區段位址 (正常值為 0x1000) sread: .word 0 ! 目前磁軌上可讀取的磁區數 head: .word 0 ! 目前的磁頭編號 track: .word 0 ! 目前的磁軌編號 read_it: mov al,setup_sects ! AL = 4 inc al ! AL = 5 mov sread,al ! mov ax,es ! AX = 1000h test ax,#0x0fff ! (1000h AND 0fffh) -> ZF=1 die: ! jne die ! ES 必須位於 64K 邊界, 否則不給通過 xor bx,bx ! bx is starting address within segment rp_read: #ifdef __BIG_KERNEL__ #define \ ! 叫用far* bootsect_kludge(as86無法組譯這段碼) CALL_HIGHLOAD_KLUDGE .word 0x1eff,0x220 CALL_HIGHLOAD_KLUDGE ! this is within setup.S #else mov ax,es sub ax,#SYSSEG ! AX = ES - 1000h #endif cmp ax,syssize ! have we loaded all yet? jbe ok1_read ! 若 AX<=8000h 則繼續讀取 ret ok1_read: mov ax,sectors ! AX = 磁軌上的磁區數 sub ax,sread ! mov cx,ax ! CX = 尚未讀取的磁區數 shl cx,#9 ! CX *= 512 add cx,bx ! CX += 目前已讀到的段內偏移位置 ES:BX jnc ok2_read ! je ok2_read ! 若 CX<=64K 則繼續讀取 xor ax,ax sub ax,bx shr ax,#9 ok2_read: call read_track ! 傳入 es:bx=緩衝區, AL=欲讀取的磁區數 mov cx,ax ! CX = 已讀入的磁區數 add ax,sread ! AX = 目前磁軌上的磁區數 cmp ax,sectors ! jne ok3_read ! 若尚未讀完目前磁軌上的磁區, 則跳到 ok3_read mov ax,#1 sub ax,head jne ok4_read ! 若目前位於第 0 磁頭, 則跳到 ok4_read inc track ! 已讀完目前磁軌上的磁區, 換下一個 ok4_read: mov head,ax ! 切換磁頭 xor ax,ax ! AX = 0 (因為尚未開始讀取) ok3_read: mov sread,ax ! sread = 目前磁軌上的尚未讀取的磁區數 shl cx,#9 ! 算出之前讀入的資料量 add bx,cx ! 調整緩衝區偏移指標 jnc rp_read ! 若 BX < 64K 則繼續重複讀取 mov ax,es ! add ah,#0x10 ! 否則將 ES +=10, mov es,ax ! 也就是位移到下一個 64K 的開頭 xor bx,bx ! jmp rp_read ! 並繼續重複讀取 read_track: pusha pusha mov ax, #0xe2e ! 在螢幕上印出 '.' mov bx, #7 int 0x10 popa mov dx,track ! DX = 目前的磁軌編號 mov cx,sread ! inc cx ! CX = 欲讀取的起始磁區編號 (從1開始算) mov ch,dl ! CH = 磁軌編號 mov dx,head ! mov dh,dl ! DH = 目前的磁頭編號 and dx,#0x0100 ! DL = 0 (磁碟機編號 A) mov ah,#2 push dx ! save for error dump push cx push bx push ax int 0x13 ! 載入磁區 jc bad_rt ! 成功了嗎? add sp, #8 ! 取消上面推入的 DX,CX,BX,AX popa ret bad_rt: push ax ! save error code call print_all ! ah = error, al = read xor ah,ah xor dl,dl int 0x13 add sp, #10 popa jmp read_track /* print_all 用於除錯, 他可被副程式叫用, 印出所有的暫存器內容, * 當叫用她時, 堆疊內應具有如下的結構: * dx * cx * bx * ax * error * ret <- sp */ print_all: mov cx, #5 ! error code + 4 registers mov bp, sp print_loop: push cx ! 保存剩餘的計數值 call print_nl ! 換行顯示, 以加強可讀性 cmp cl, #5 jae no_reg ! see if register name is needed mov ax, #0xe05+'A-1 ! 依序印 D,C,B,A sub al, cl int 0x10 mov al, #'X int 0x10 mov al, #': int 0x10 no_reg: add bp, #2 ! next register call print_hex ! print it pop cx loop print_loop ret print_nl: mov ax, #0xe0d ! CR int 0x10 mov al, #0xa ! LF int 0x10 ret // print_hex 用於除錯, 會以 16 進位數值印出 [ss:bp] print_hex: mov cx, #4 ! 4 位數的 hex mov dx, (bp) ! load word into dx print_digit: rol dx, #4 ! rotate so that lowest 4 bits are used mov ax, #0xe0f ! ah = request, al = mask for nybble and al, dl add al, #0x90 ! convert al to ASCII hex (four instructions) daa ! 轉成 10 進制 adc al, #0x40 daa int 0x10 loop print_digit ret // 這段程式可關閉軟碟機的馬達, 使得我們進入核心後, // 其狀態是已知的. (若不關的話會怎樣呢?) kill_motor: push dx mov dx,#0x3f2 xor al, al outb pop dx ret // 底下是變數群: sectors: .word 0 disksizes: .byte 36,18,15,9 msg1: .byte 13,10 .ascii "Loading" .byte 0 .org 497 //底下變數由本程式的 497 Bytes 偏移處開始存放 setup_sects: .byte SETUPSECS root_flags: .word CONFIG_ROOT_RDONLY syssize: .word SYSSIZE swap_dev: .word SWAP_DEV ram_size: .word RAMDISK vid_mode: .word SVGA_MODE root_dev: .word ROOT_DEV boot_flag: .word 0xAA55
沒有留言:
張貼留言