Heim >Datenbank >MySQL-Tutorial >FreeBSD启动扇区代码分析(ver5.2.1)

FreeBSD启动扇区代码分析(ver5.2.1)

WBOY
WBOYOriginal
2016-06-07 15:24:451383Durchsuche

FreeBSD启动扇区代码分析(ver5.2.1) 2007-04-27 来源:cnfug 作者: 关键词: FreeBSD代码BSD扇区分析 FreeBSD完整的内核代码在FreeBSD的/sys目录下。其中,FreeBSD 的 Boot Manager代码是 sys/boot/i386/boot0/boot0.s,它是FreeBSD自带的Boot Manager,其功

FreeBSD启动扇区代码分析(ver5.2.1)

2007-04-27      来源:cnfug         作者:

关键词:   FreeBSD      代码      BSD      扇区      分析   

FreeBSD完整的内核代码在FreeBSD的 “/sys”目录下。其中,FreeBSD 的 Boot Manager代码是 sys/boot/i386/boot0/boot0.s,它是FreeBSD自带的Boot Manager,其功能虽然不如Linux的lilo和Grub功能强大,但它只需按一个键就可以引导,界面直观。小小的512字节,可以给你一个简单明 了的启动选择界面,还能记住你上次的选择。以下,就对此代码进行详细分析:

当我们打开计算机电源时,计算机叽叽嘎嘎进行设备和内 存检测过后就读取硬盘或者软盘的引导扇区,这个扇区只有512字节,显然这512字节不能够有多大作用,这512字节的代码被BIOS放在地址从 0x0000:0x7c00开始处。然后直接跳转到0x0000:0x7c00处去执行。以上工作是BIOS 干的,你什么也不用作。操作系统需要通过这个引导扇区代码再装载操作系统的其他部分。 在还没有跳转到这段代码之前,也就是BIOS把磁盘的引导扇区读入到内存之后,其DL和ES、SI寄存器的内容如下: DL:表示启动设备,例如,如果计算机是从软盘启动的则DL=0,若是从IDE的C、D盘(严格来说是物理磁盘一和物理磁盘二,而不是逻辑磁盘分区)启动 的则DL分别为0x80和0x81。如果是从硬盘启动的话,ES:SI是指向BIOS中的硬盘分区表存放的地址。

好了,我们现在已经知道,计算机的BIOS已经把引导扇区的512字节的内容读入到了0:0x7c00处,然后就跳转到0:0x7C00处去执行,也就是执行引导扇区代码,引导扇区代码boot0执行代码dump如下(它很有用,以后我们还不时回头来看):

560)this.style.width=560;'' onmousewheel = ''javascript:return big(this)'' height=517 alt="" src="http://images.51cto.com/files/uploadimg/20051027/104330627.gif" width=590 border=0>

上图中的4x16个字节是保留的4个分区信息

下面,我们对FreeBSD启动扇区代码boot0.s进行逐步分析。boot0.s代码如下:

<span><span><span><span>#<br>
# Copyright (c) 1998 Robert Nordier<br>
# All rights reserved.<br>
#<br>
# Redistribution and use in source and binary forms are freely<br>
# permitted provided that the above copyright notice and this<br>
# paragraph and the following disclaimer are duplicated in all<br>
# such forms.<br>
#<br>
# This software is provided "AS IS" and without any express or<br>
# implied warranties, including, without limitation, the implied<br>
# warranties of merchantability and fitness for a particular<br>
# purpose.<br>
#<br>
以上的Coyright就不用翻译了。<br>
<br>
# $FreeBSD: src/sys/boot/i386/boot0/boot0.s,v 1.27 2003/11/20 20:28:18 jhb Exp $<br>
以上供版本管理软件使用<br>
 <br>
<br>
# A 512-byte boot manager.<br>
<br>
        .set NHRDRV,0x475     # Number of hard drives<br>
        .set ORIGIN,0x600     # Execution address<br>
        .set FAKE,0x800       # Partition entry<br>
        .set LOAD,0x7c00      # Load address<br>
<br>
        .set PRT_OFF,0x1be     # Partition table<br>
<br>
        .set TBL0SZ,0x3     # Table 0 size<br>
        .set TBL1SZ,0xb     # Table 1 size<br>
<br>
        .set MAGIC,0xaa55     # Magic: bootable<br>
        .set B0MAGIC,0xbb66     # Identification<br>
<br>
        .set KEY_ENTER,0x1c     # Enter key scan code<br>
        .set KEY_F1,0x3b         # F1 key scan code<br>
        .set KEY_1,0x02         # #1 key scan code<br>
#<br>
# Addresses in the sector of embedded data values.<br>
# Accessed with negative offsets from the end of the relocated sector (%ebp).<br>
#<br>
        .set _NXTDRV,-0x48         # Next drive<br>
        .set _OPT,-0x47             # Default option<br>
        .set _SETDRV,-0x46         # Drive to force<br>
        .set _FLAGS,-0x45         # Flags<br>
        .set _TICKS,-0x44         # Timeout ticks<br>
        .set _FAKE,0x0             # Fake partition entry<br>
        .set _MNUOPT,0xc # Menu options<br>
 <br>
<br>
以上是定义相关的参数值,例如“.set NHRDRV,0x475”类似于C语言中的“#define NHRDRV 0x475”<br>
<br>
        .globl start     # Entry point<br>
        .code16          # This runs in real mode<br>
<br>
#<br>
# Initialise segments and registers to known values.<br>
# segments start at 0.<br>
# The stack is immediately below the address we were loaded to.<br>
#<br>
start:<br>
        cld             # String ops inc<br>
        xorw %ax,%ax     # Zero<br>
        movw %ax,%es     # Address<br>
        movw %ax,%ds     #  data<br>
        movw %ax,%ss     # Set up<br>
        movw $LOAD,%sp     #  stack<br>
 <br>
<br>
以上代码:<br>
1)首先使用“cld”指令清除方向标志,使得以下的进行“rep”操作时SI和DI的值递增。<br>
2)使ax清零,并使除代码段cs外的另外两个数据段寄存器es、ds和堆栈段ss清零。当然,此时cs<br>
   由于reset或初始上电已经为零了。<br>
3)BIOS已经把引导扇区的512字节的内容读入到了0:0x7c00处,movw $LOAD,%sp 使得堆栈指针指向扇区<br>
   代码(或曰本段代码 0:0x7c00)的顶部。虽然堆栈向下生长可能会影响代码的内容,但下面我<br>
   们马上就把位于0:7c00处代码移到其他地方去执行。<br>
 <br>
<br>
#<br>
# Copy this code to the address it was linked for<br>
#<br>
        movw %sp,%si         # Source<br>
        movw $start,%di     # Destination<br>
        movw $0x100,%cx     # Word count<br>
        rep                 # Relocate<br>
        movsw                 #  code<br>
<br>
把位于0:7c00处的代码搬移到0:0x600处。注意,此时由于代码连接的重定向,$start=0x600。<br>
<br>
#<br>
# Set address for variable space beyond code, and clear it.<br>
# Notice that this is also used to point to the values embedded in the block,<br>
# by using negative offsets.<br>
        movw %di,%bp     # Address variables<br>
        movb $0x8,%cl     # Words to clear<br>
        rep             # Zero<br>
        stosw             #  them<br>
<br>
通过以上一段代码的执行,本代码已被搬移到0:0x600处,此时si=di=0x600+0x100,以上代码<br>
把di的值保存到bp,bp此时指向本程序搬移后的未用的空间的首部,且把此bp所指的16字节空间<br>
清零。以上过程如下图所示:<br>
<br>
                        ┏>0:0x600 ┏━━━━━┓<br>
                        ┃         ┃          ┃<br>
                        ┃         ┃    搬    ┃<br>
                        ┃         ┃    移    ┃<br>
                        ┃         ┃    之    ┃<br>
                        ┃         ┃    后    ┃<br>
                        ┃         ┃    的    ┃<br>
                        ┃         ┃    代    ┃<br>
                        ┃         ┃    码    ┃<br>
                        ┃         ┃          ┃<br>
                        ┃ 0:0x7ff ┣━━━━━┫<br>
                        ┃         ┃    0     ┃
                        ┃         ┣━━━━━┫以下所称的fake partition entry就是指这里。<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┣━━━━━┫<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┣━━━━━┫<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┣━━━━━┫<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┣━━━━━┫<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┣━━━━━┫<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┣━━━━━┫<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┣━━━━━┫<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┣━━━━━┫<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┣━━━━━┫<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┣━━━━━┫<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┣━━━━━┫<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┣━━━━━┫<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┣━━━━━┫<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┣━━━━━┫<br>
                        ┃         ┃    0     ┃<br>
                        ┃         ┗━━━━━┛<br>
0:0x7c00 ┏━━━━━┓ ┛<br>
         ┃          ┃<br>
         ┃    搬    ┃<br>
         ┃    移    ┃<br>
         ┃    之    ┃<br>
         ┃    前    ┃<br>
         ┃    的    ┃<br>
         ┃    代    ┃<br>
         ┃    码    ┃<br>
         ┃          ┃<br>
0:0x7dff ┗━━━━━┛<br>
<br>
                      图(二)<br>
#<br>
# Relocate to the new copy of the code.<br>
#<br>
        incb -0xe(%di) # Sector number<br>
        jmp main-LOAD+ORIGIN # To relocated code<br>
<br>
把以上清零的16字节的第二个字节置为1,表示我们已经读取了一个分区。然后跳转到搬<br>
移之后的新代码的main处执行。<br>
<br>
#<br>
# Check what flags were loaded with us, specifically, Use a predefined Drive.<br>
# If what the bios gives us is bad, use the ''0'' in the block instead, as well.<br>
#<br>
main:<br>
        testb $0x20,_FLAGS(%bp)       # Set number drive?<br>
        jnz main.1                     # Yes<br>
        testb %dl,%dl                 # Drive number valid?<br>
        js main.2                     # Possibly (0x80 set)<br>
main.1:<br>
        movb _SETDRV(%bp),%dl # Drive number to use<br>
上面说过,BIOS把磁盘的引导扇区读入到内存之后,其dl的内容表示启动设备,但我们安装好FreeBSD<br>
之后,我们可以改变此引导扇区的内容,其中的一个改变就是可以使我们可以“手动指定”我们实际安<br>
装FreeBSD的分区,如果我们希望指定FreeBSD所在的boot分区,那么我们在bp-0x45处的位置<br>
(即_FLAGS(%bp)处)的bit 0x20置1,那么上面的“movb _SETDRV(%bp),%dl”一句中movb<br>
_SETDRV(%bp),%dl(即bp-0x46)即指向我们“手动指定”FreeBSD所在分区代码,例如,IDE的C、D<br>
盘(严格来说是第一个物理磁盘的第一个和第二个分区)的代码分别为 0x80和0x81。如果没有“手动指<br>
定”启动分区,那么,我们缺省使用机器当前启动的分区,上面说过,机器当前启动的分区代码放在dl中。<br>
<br>
因为FreeBSD Boot Manager 不可能安装到软盘(如果从软盘启动则dl为0),所以,使用testb %dl,%dl<br>
来判断驱动器代码是否合法(volid)。<br>
<br>
有关_FLAGS(%bp)中其他bit位表示的意义,在随后的代码分析中慢慢给你道来。<br>
<br>
#<br>
# Whatever we decided to use, now store it into the fake<br>
# partition entry that lives in the data space above us.<br>
#<br>
main.2:<br>
        movb %dl,_FAKE(%bp) # Save drive number<br>
        callw putn # To new line<br>
        pushw %dx # Save drive number<br>
<br>
以上第一句把FreeBSD启动分区的代码保存到_FAKE(%bp)(bp-0)处,也就是说,上图(二)的bp<br>
处保存的是FreeBSD启动分区的代码(_FAKE=0)。<br>
<br>
“callw putn”一句在屏幕上打印“回车”和“换行”,“pushw %dx”一句把启动分区<br>
的值压入堆栈。<br>
#<br>
# Start out with a pointer to the 4th byte of the first table entry<br>
# so that after 4 iterations it''s beyond the end of the sector.<br>
# and beyond a 256 byte boundary and has overflowed 8 bits (see next comment).<br>
# (remember that the table starts 2 bytes earlier than you would expect<br>
# as the bootable flag is after it in the block)<br>
#<br>
        movw $(partbl+0x4),%bx # Partition table (+4)<br>
        xorw %dx,%dx # Item number<br>
<br>
以上代码首先把%bx指向分区表partbl的的第四个字节,这里存放的是分区类型,如82表示<br>
Linux Native分区83表示Linux Swap 分区,有关分区表的细节请详见本文的尾部。然后dx清零,<br>
此后,dx将作为遍历磁盘分区的列举代号使用。启动分区代码dl的原来的值在上面已经压入<br>
了堆栈保存。<br>
<br>
#<br>
# Loop around on the partition table, printing values until we<br>
# pass a 256 byte boundary. The end of loop test is at main.5.<br>
#<br>
main.3:<br>
        movb %ch,-0x4(%bx)     # Zero active flag (ch == 0)<br>
        btw %dx,_FLAGS(%bp)     # Entry enabled?<br>
        jnc main.5             # No<br>
上面首先使得第一个分区的活动标志为0,标志它不为活动标志,因为ch的值为0。至<br>
于第二句“btw %dx,_FLAGS(%bp)”中的_FLAGS(%bp)是上面我们说到的“手动指定我们<br>
实际安装FreeBSD的分区代码”。其中的bit 0x20我们在上面已经提到过。_FLAGS(%bp)<br>
中的其他位表示是否我们需要检查相应的磁盘分区。缺省情况下,我们需要检查所有<br>
的磁盘分区。检查磁盘分区看是否有可以启动的磁盘分区,例如,可能磁盘上的某个<br>
分区为WindowsXP或者是Linux等。如果我们没有改变在磁盘上该处的值,则相应的bit<br>
位的值为0,表示所有的分区都要检查(因为此时_FLAGS(%bp)中的值为0),否则,<br>
只针对FLAGS(%bp)中相应的bit位未被设置为1的分区进行检查。<br>
<br>
大家知道,FreeBSD Manager启动时可能出现以下的提示:<br>
<br>
F1    FreeBSD<br>
F2    ??<br>
F3    BSD<br>
F4    ??<br>
Default    F1<br>
<br>
其中,上面的提示中出现了令人讨厌的“??”,为了避免出现“??”的提示,我们可以设置相应的<br>
第一分区和第四分区不检查,就需要正确设置_FLAGS(%bp)中的bit位。设置好后,屏幕可能<br>
出现以下的提示:<br>
<br>
F1    FreeBSD<br>
F2    BSD<br>
Default    F1<br>
<br>
#<br>
# If any of the entries in the table are<br>
# the same as the ''type'' in the slice table entry,<br>
# then this is an empty or non bootable partition. Skip it.<br>
#<br>
        movb (%bx),%al         # Load type<br>
        movw $tables,%di     # Lookup tables<br>
        movb $TBL0SZ,%cl     # Number of entries<br>
        repne                 # Exclude<br>
        scasb                 #  partition?<br>
        je main.5             # Yes<br>
我们从上面已经知道起始(%bx)指向的是MBR中分区信息1(16字节)的位置(见图(三)),<br>
以上代码在“忽略的分区类型$tables”中查找看是否本分区是不可启动的或者不合法的分区。<br>
不可启动的或者不合法的分区类型有3($TBL0SZ=3)个,它们是“0x0, 0x5, 0xf”,见下面的<br>
$tables处。如果是不可启动的或者不合法的分区类型则跳转到main.5,进行下一轮循环。<br>
<br>
#<br>
# Now scan the table of known types<br>
#<br>
        movb $TBL1SZ,%cl         # Number of entries<br>
        repne                     # Known<br>
        scasb                     #  type?<br>
        jne main.4                 # No<br>
#<br>
# If it matches get the matching element in the<br>
# next array. if it doesn''t, we are already<br>
# pointing at its first element which points to a "?".<br>
#<br>
        addw $TBL1SZ,%di     # Adjust<br>
main.4:<br>
        movb (%di),%cl # Partition<br>
        addw %cx,%di #  description<br>
        callw putx # Display it<br>
上面检查看所检查的分区类型是否为我们知道的分区类型,知道的分区类型有11($TBL1SZ=0xb)<br>
个,它们是:“0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83,0x9f, 0xa5, 0xa6, 0xa9”,见<br>
下面的$tables处。如果不是以上的类型,则跳转到main.4。那么,(%di)所指的字串是“??”,<br>
如果分区类型是“0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83,0x9f, 0xa5, 0xa6, 0xa9”<br>
之一,则(%di)所指的字串是“DOS”、“Linux”、“FreeBSD”或“BSD”等。<br>
见下面的“os_misc”、“os_dos”、“os_linux”、“os_freebsd”、“os_bsd”等<br>
标记。<br>
<br>
callw putx调用putx函数,在屏幕上打印:“Fx   XXX”。其中XXX为DOS”、“Linux”、<br>
“FreeBSD”或“BSD”等。<br>
<br>
main.5:<br>
        incw %dx # Next item<br>
        addb $0x10,%bl # Next entry<br>
        jnc main.3 # Till done<br>
遍历磁盘分区的举代号dx加1,重复下一轮循环查找。bl加上0x10(0x10=16)表示寻址到下<br>
一个分区信息(加16字节)入口。循环直到255字节边界。<br>
<br>
#<br>
# Passed a 256 byte boundary..<br>
# table is finished.<br>
# Add one to the drive number and check it is valid,<br>
#<br>
        popw %ax                 # Drive number<br>
        subb $0x80-0x1,%al     # Does next<br>
        cmpb NHRDRV,%al         #  drive exist? (from BIOS?)<br>
        jb main.6                 # Yes<br>
“popw %ax”把上面压入堆栈的bx(当前的启动扇区)值弹出到ax中。例如,如果计算机是从软盘<br>
启动的则dl=0,若是从IDE的C、D盘启动的则dl分别为 0x80和0x81。然而,FreeBSD的Boot Manerger不能够<br>
安装到软盘上,所以,dl只能为0x80、0x81,0x82...等。<br>
在计算机的BIOS地址0:0x475处存放的是计算机的硬盘的数目,“subb $0x80-0x1,%al”一句等于“sub<br>
$0x79,%al”,例如,即当前驱动器如果是C盘,则al的值是ox80-0x79=1,然后再与计算机的硬盘的数目比<br>
较,如果当前所在硬盘不是最后一个硬盘,则直接跳转到main.6。如果当前所在硬盘是最后一个硬盘,则继<br>
续执行。<br>
<br>
# If not then if there is only one drive,<br>
# Don''t display drive as an option.<br>
#<br>
        decw %ax         # Already drive 0?<br>
        jz main.7         # Yes<br>
如果只有一个硬盘,则直接跳转到main.7,这样,本计算机只有一个硬盘,所以不用显示其他<br>
磁盘相关的提示。<br>
<br>
# If it was illegal or we cycled through them,<br>
# then go back to drive 0.<br>
#<br>
        xorb %al,%al # Drive 0<br>
<br>
下面的内容表示多于一个磁盘的情况。此时“al”清0,与磁盘列举相关。<br>
#<br>
# Whatever drive we selected, make it an ascii digit and save it back<br>
# to the "next drive" location in the loaded block in case we<br>
# want to save it for next time.<br>
# This also is part of the printed drive string so add 0x80 to indicate<br>
# end of string.<br>
#<br>
main.6:<br>
        addb $''0''|0x80,%al         # Save next<br>
        movb %al,_NXTDRV(%bp)         #  drive number<br>
        movw $drive,%di             # Display<br>
        callw putx             #  item<br>
首先,在_NXTDR(%bp)处置入“0字符高位置1”的字符,以代表第二个驱动器,<br>
然后在屏幕上显示“Fx Drive”,表示更换另外的磁盘启动。注意,在调用<br>
putx之前,di中保存的是下面字串“Drive ”的首地址。dl中存放的是当前<br>
遍历的到的可启动的或者合法的分区类型递增序数,al与dl是不同的,al是ASCII码,<br>
dl是“Fx”中的x值。<br>
<br>
#<br>
# Now that we''ve printed the drive (if we needed to), display a prompt.<br>
# Get ready for the input byt noting the time.<br>
#<br>
main.7:<br>
        movw $prompt,%si     # Display<br>
        callw putstr         #  prompt<br>
        movb _OPT(%bp),%dl     # Display<br>
        decw %si             #  default<br>
        callw putkey         #  key<br>
        xorb %ah,%ah         # BIOS: Get<br>
        int $0x1a             #  system time<br>
        movw %dx,%di             # Ticks when<br>
        addw _TICKS(%bp),%di   #  timeout<br>
上面的代码首先在屏幕上打印出字符串“Default: ”,缺省启动的磁盘号放在<br>
“_OPT(%bp)”中,这里有个小小的技巧,在执行“decw %si”和“callw putkey”<br>
两句后屏幕会显示“Fx”,x是_OPT(%bp)的ASCII。<br>
<br>
然后取得当前的tickes放到%di中,等待用户按键超时的时间从_TICKS(%bp)中取出,<br>
加到当前的tickes即是最后超时时间到的tickes。<br>
<br>
#<br>
# Busy loop, looking for keystrokes but<br>
# keeping one eye on the time.<br>
#<br>
main.8:<br>
        movb $0x1,%ah     # BIOS: Check<br>
        int $0x16         #  for keypress<br>
        jnz main.11         # Have one<br>
        xorb %ah,%ah         # BIOS: Get<br>
        int $0x1a         #  system time<br>
        cmpw %di,%dx         # Timeout?<br>
        jb main.8         # No<br>
等待用户按下“Fx”键,同时检查当前等待是否超时,如果有用户按键则跳转到main.11,<br>
如果超时时间不到则继续等待。<br>
<br>
#<br>
# If timed out or defaulting, come here.<br>
#<br>
main.9:<br>
        movb _OPT(%bp),%al # Load default<br>
        jmp main.12 # Join common code<br>
超时时间到,此时表示用户使用缺省分区启动,把缺省的启动分区号置入al中,然后跳转<br>
到main.12。<br>
<br>
#<br>
# User''s last try was bad, beep in displeasure.<br>
# Since nothing was printed, just continue on as if the user<br>
# hadn''t done anything. This gives the effect of the user getting a beep<br>
# for all bad keystrokes but no action until either the timeout<br>
# occurs or the user hits a good key.<br>
#<br>
main.10:<br>
        movb $0x7,%al # Signal<br>
        callw putchr #  error<br>
用户输入错误,只是响铃提示,其他什么也不发生。<br>
<br>
#<br>
# Get the keystroke.<br>
#<br>
main.11:<br>
        xorb %ah,%ah         # BIOS: Get<br>
        int $0x16             # keypress<br>
        movb %ah,%al         # Scan code<br>
用户按下了一个键,把键值扫描码放到al中。<br>
<br>
#<br>
# If it''s CR act as if timed out.<br>
#<br>
        cmpb $KEY_ENTER,%al         # Enter pressed?<br>
        je main.9                 # Yes<br>
如果用户按下“Enter”键,和超时等同处理,这样,就启动缺省的boot分区。<br>
<br>
#<br>
# Otherwise check if legal<br>
# If not ask again.<br>
#<br>
        subb $KEY_F1,%al         # Less F1 scan code<br>
        cmpb $0x4,%al             # F1..F5?<br>
        jna main.12                 # Yes<br>
        subb $(KEY_1 - KEY_F1),%al         # Less #1 scan code<br>
        cmpb $0x4,%al                 # #1..#5?<br>
        ja main.10                     # No<br>
如果是除“Enter”键外其他的键,则检查是不是F1...F5键,如果不是,<br>
表示输入不合法,跳回到main.10处理。<br>
<br>
#<br>
# We have a selection.<br>
# but if it''s a bad selection go back to complain.<br>
# The bits in MNUOPT were set when the options were printed.<br>
# Anything not printed is not an option.<br>
#<br>
main.12:<br>
        cbtw                     # Option<br>
        btw %ax,_MNUOPT(%bp)   #  enabled?<br>
        jnc main.10             # No<br>
如果是F1...F5键,则检查是否在我们提示的范围内,其中,_MNUOPT(%bp)的相应<br>
bit位为1,表示是一个合法的选项,如果不是,跳回到 main.10处理。<br>
<br>
#<br>
# Save the info in the original tables<br>
# for rewriting to the disk.<br>
#<br>
        movb %al,_OPT(%bp)         # Save option<br>
把我们按下的F1...F5键保存到_OPT(%bp)位置。<br>
<br>
        movw $FAKE,%si             # Partition for write<br>
        movb (%si),%dl             # Drive number<br>
把原来的启动分区代码取回到dl中。<br>
<br>
        movw %si,%bx                 # Partition for read<br>
        cmpb $0x4,%al                 # F5 pressed?<br>
        pushf                     # Save<br>
        je main.13             # Yes<br>
如果我们按下的是F5键则直接跳转到main.13处理。<br>
<br>
        shlb $0x4,%al             # Point to<br>
        addw $partbl,%ax             #  selected<br>
        xchgw %bx,%ax               #  partition<br>
        movb $0x80,(%bx)             # Flag active<br>
上面,我们从按键Fx选择中得到图(三)中的我们选择的四个分区信息中的某一分区信息,<br>
上面计算出的bx为我们选择的分区信息的首地址,我们把此选择到的分区信息的第一个<br>
个字节置为0x80表示它是当前的活动分区。<br>
#<br>
# If not asked to do a write-back (flags 0x40) don''t do one.<br>
#<br>
main.13:<br>
        pushw %bx                     # Save<br>
        testb $0x40,_FLAGS(%bp)         # No updates?<br>
        jnz main.14                 # Yes<br>
        movw $start,%bx         # Data to write<br>
        movb $0x3,%ah             # Write sector<br>
        callw intx13             #  to disk<br>
检查回写标志_FLAGS(%bp)的bit位0x40为,如果设置的是可回写,则把当前选择到的boot<br>
分区作为下次缺省的启动分区。<br>
<br>
main.14:<br>
        popw %si             # Restore<br>
        popf                 # Restore<br>
<br>
#<br>
# If going to next drive, replace drive with selected one.<br>
# Remember to un-ascii it. Hey 0x80 is already set, cool!<br>
#<br>
        jne main.15                 # If not F5<br>
恢复上面保存的si和标志寄存器的内容。如果不是按键F5,则直接跳转到main.15去执行。<br>
<br>
        movb _NXTDRV(%bp),%dl         # Next drive<br>
        subb $''0'',%dl                 #  number<br>
<br>
否则的话,我们选择下一个驱动器作为启动盘。<br>
#<br>
# load  selected bootsector to the LOAD location in RAM.<br>
# If it fails to read or isn''t marked bootable, treat it<br>
# as a bad selection.<br>
# XXX what does %si carry?<br>
#<br>
main.15:<br>
        movw $LOAD,%bx         # Address for read<br>
        movb $0x2,%ah             # Read sector<br>
        callw intx13         #  from disk<br>
        jc main.10             # If error<br>
把我们上面选择到的分区读到0x7c00处,就象我们刚刚才加电启动一样,只是活动分区改变<br>
了而已。如果发生读错误则直接跳转到main.10。使用户重新选择启动分区。<br>
<br>
        cmpw $MAGIC,0x1fe(%bx)         # Bootable?<br>
        jne main.10             # No<br>
判断引导扇区的最后两个字节是否是“0x55AA”,如果不是,则表示此扇区是不可引导的,或<br>
不合法的引导扇区则直接跳转到main.10。使用户重新选择启动分区。<br>
<br>
        pushw %si             # Save<br>
        movw $crlf,%si         # Leave some<br>
        callw puts             #  space<br>
        popw %si             # Restore<br>
<br>
打印“回车”和“换行”。<br>
<br>
        jmp *%bx             # Invoke bootstrap<br>
<br>
跳转到我们选择的引导扇区去执行。整个Boot Manager代码到此执行完毕。<br>
<br>
#<br>
# Display routines<br>
#<br>
<br>
putkey:<br>
        movb $''F'',%al         # Display<br>
        callw putchr         #  ''F''<br>
        movb $''1'',%al         # Prepare<br>
        addb %dl,%al         #  digit<br>
        jmp putstr.1         # Display the rest<br>
“putkey”函数在屏幕上打印“F1”、“F2”或“F3”等。如果dl为0则打印“F1”,<br>
如果dl为1则打印“F2”,如果dl为3则打印“F3”。和调用“putstr”在屏幕上打印<br>
es:si指向的以最高位置1为结束字符的字符串。<br>
#<br>
# Display the option and note that it is a valid option.<br>
# That last point is a bit tricky..<br>
#<br>
putx: #首先,把_MNUOPT(%bp)的第dx位(bit)置1,表示此菜单选项被显示。然后在<br>
屏幕上打印空格和es:di指向的以最高位置1为结束字符的字符串。<br>
        btsw %dx,_MNUOPT(%bp)         # Enable menu option<br>
        movw $item,%si                 # Display<br>
        callw putkey                 #  key<br>
        movw %di,%si                 # Display the rest<br>
<br>
puts: #调用“putstr”在屏幕上打印es:si指向的以最高位置1为结束字符的字符串。<br>
        callw putstr                 # Display string<br>
<br>
putn: #“putn”打印“回车/换行”后在屏幕上打印es:si指向的以最高位置1为结束字符的字符串。<br>
        movw $crlf,%si                 # To next line<br>
<br>
putstr: #“putstr”在屏幕上打印es:si指向的以最高位置1为结束字符的字符串。<br>
        lodsb                     # Get byte<br>
        testb $0x80,%al         # End of string?<br>
        jnz putstr.2             # Yes<br>
putstr.1:<br>
        callw putchr             # Display char<br>
        jmp putstr                 # Continue<br>
putstr.2:<br>
        andb $~0x80,%al             # Clear MSB<br>
<br>
putchr:<br>
        pushw %bx                 # Save<br>
        movw $0x7,%bx           # Page:attribute<br>
        movb $0xe,%ah             # BIOS: Display<br>
        int $0x10                 #  character<br>
        popw %bx                 # Restore<br>
        retw                     # To caller<br>
“putchr”在屏幕上显示“al”中的字符。<br>
# One-sector disk I/O routine<br>
<br>
intx13:<br>
        movb 0x1(%si),%dh             # Load head<br>
        movw 0x2(%si),%cx             # Load cylinder:sector<br>
        movb $0x1,%al                 # Sector count<br>
        pushw %si                     # Save<br>
        movw %sp,%di                 # Save<br>
        testb $0x80,_FLAGS(%bp)         # Use packet interface?<br>
        jz intx13.1                 # No<br>
        pushl $0x0                 # Set the<br>
        pushl 0x8(%si)             # LBA address<br>
        pushw %es                 # Set the transfer<br>
        pushw %bx                 #  buffer address<br>
        push  $0x1                 # Block count<br>
        push  $0x10                 # Packet size<br>
        movw %sp,%si             # Packet pointer<br>
        decw %ax                 # Verify off<br>
        orb $0x40,%ah             # Use disk packet<br>
intx13.1:<br>
        int $0x13                 # BIOS: Disk I/O<br>
        movw %di,%sp             # Restore<br>
        popw %si                 # Restore<br>
        retw                     # To caller<br>
<br>
# Menu strings<br>
<br>
item:<br>
        .ascii "  ";      .byte '' ''|0x80<br>
prompt:<br>
        .ascii "/nDefault:"; .byte '' ''|0x80<br>
crlf:<br>
        .ascii "/r";      .byte ''/n''|0x80<br>
<br>
# Partition type tables<br>
<br>
tables:<br>
#<br>
# These entries identify invalid or NON BOOT types and partitions.<br>
#<br>
        .byte 0x0, 0x5, 0xf<br>
#<br>
# These values indicate bootable types we know the names of<br>
#<br>
        .byte 0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83<br>
        .byte 0x9f, 0xa5, 0xa6, 0xa9<br>
#<br>
# These are offsets that match the known names above and point to the strings<br>
# that will be printed.<br>
#<br>
        .byte os_misc-.         # Unknown<br>
        .byte os_dos-.         # DOS<br>
        .byte os_dos-.         # DOS<br>
        .byte os_dos-.         # DOS<br>
        .byte os_dos-.         # Windows<br>
        .byte os_dos-.         # Windows<br>
        .byte os_dos-.         # Windows<br>
        .byte os_linux-.         # Linux<br>
        .byte os_bsd-.         # BSD/OS<br>
        .byte os_freebsd-.         # FreeBSD<br>
        .byte o <br>
(完)</span>
</span>
</span>
</span>


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn