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

php中文网
发布: 2016-06-07 15:24:45
原创
1558人浏览过

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 />
以上是定义相关的参数值,例如&ldquo;.set NHRDRV,0x475&rdquo;类似于C语言中的&ldquo;#define NHRDRV 0x475&rdquo;<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)首先使用&ldquo;cld&rdquo;指令清除方向标志,使得以下的进行&ldquo;rep&rdquo;操作时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     ┃<-bp指向这里(0:0x800),以此开始的16字节被清零。<br />
                        ┃         ┣━━━━━┫以下所称的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 />
之后,我们可以改变此引导扇区的内容,其中的一个改变就是可以使我们可以&ldquo;手动指定&rdquo;我们实际安<br />
装FreeBSD的分区,如果我们希望指定FreeBSD所在的boot分区,那么我们在bp-0x45处的位置<br />
(即_FLAGS(%bp)处)的bit 0x20置1,那么上面的&ldquo;movb _SETDRV(%bp),%dl&rdquo;一句中movb<br />
_SETDRV(%bp),%dl(即bp-0x46)即指向我们&ldquo;手动指定&rdquo;FreeBSD所在分区代码,例如,IDE的C、D<br />
盘(严格来说是第一个物理磁盘的第一个和第二个分区)的代码分别为 0x80和0x81。如果没有&ldquo;手动指<br />
定&rdquo;启动分区,那么,我们缺省使用机器当前启动的分区,上面说过,机器当前启动的分区代码放在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 />
&ldquo;callw putn&rdquo;一句在屏幕上打印&ldquo;回车&rdquo;和&ldquo;换行&rdquo;,&ldquo;pushw %dx&rdquo;一句把启动分区<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 />
于第二句&ldquo;btw %dx,_FLAGS(%bp)&rdquo;中的_FLAGS(%bp)是上面我们说到的&ldquo;手动指定我们<br />
实际安装FreeBSD的分区代码&rdquo;。其中的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 />
其中,上面的提示中出现了令人讨厌的&ldquo;??&rdquo;,为了避免出现&ldquo;??&rdquo;的提示,我们可以设置相应的<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 />
以上代码在&ldquo;忽略的分区类型$tables&rdquo;中查找看是否本分区是不可启动的或者不合法的分区。<br />
不可启动的或者不合法的分区类型有3($TBL0SZ=3)个,它们是&ldquo;0x0, 0x5, 0xf&rdquo;,见下面的<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 />
个,它们是:&ldquo;0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83,0x9f, 0xa5, 0xa6, 0xa9&rdquo;,见<br />
下面的$tables处。如果不是以上的类型,则跳转到main.4。那么,(%di)所指的字串是&ldquo;??&rdquo;,<br />
如果分区类型是&ldquo;0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83,0x9f, 0xa5, 0xa6, 0xa9&rdquo;<br />
之一,则(%di)所指的字串是&ldquo;DOS&rdquo;、&ldquo;Linux&rdquo;、&ldquo;FreeBSD&rdquo;或&ldquo;BSD&rdquo;等。<br />
见下面的&ldquo;os_misc&rdquo;、&ldquo;os_dos&rdquo;、&ldquo;os_linux&rdquo;、&ldquo;os_freebsd&rdquo;、&ldquo;os_bsd&rdquo;等<br />
标记。<br />
<br />
callw putx调用putx函数,在屏幕上打印:&ldquo;Fx   XXX&rdquo;。其中XXX为DOS&rdquo;、&ldquo;Linux&rdquo;、<br />
&ldquo;FreeBSD&rdquo;或&ldquo;BSD&rdquo;等。<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 />
&ldquo;popw %ax&rdquo;把上面压入堆栈的bx(当前的启动扇区)值弹出到ax中。例如,如果计算机是从软盘<br />
启动的则dl=0,若是从IDE的C、D盘启动的则dl分别为 0x80和0x81。然而,FreeBSD的Boot Manerger不能够<br />
安装到软盘上,所以,dl只能为0x80、0x81,0x82...等。<br />
在计算机的BIOS地址0:0x475处存放的是计算机的硬盘的数目,&ldquo;subb $0x80-0x1,%al&rdquo;一句等于&ldquo;sub<br />
$0x79,%al&rdquo;,例如,即当前驱动器如果是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 />
下面的内容表示多于一个磁盘的情况。此时&ldquo;al&rdquo;清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)处置入&ldquo;0字符高位置1&rdquo;的字符,以代表第二个驱动器,<br />
然后在屏幕上显示&ldquo;Fx Drive&rdquo;,表示更换另外的磁盘启动。注意,在调用<br />
putx之前,di中保存的是下面字串&ldquo;Drive &rdquo;的首地址。dl中存放的是当前<br />
遍历的到的可启动的或者合法的分区类型递增序数,al与dl是不同的,al是ASCII码,<br />
dl是&ldquo;Fx&rdquo;中的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 />
上面的代码首先在屏幕上打印出字符串&ldquo;Default: &rdquo;,缺省启动的磁盘号放在<br />
&ldquo;_OPT(%bp)&rdquo;中,这里有个小小的技巧,在执行&ldquo;decw %si&rdquo;和&ldquo;callw putkey&rdquo;<br />
两句后屏幕会显示&ldquo;Fx&rdquo;,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 />
等待用户按下&ldquo;Fx&rdquo;键,同时检查当前等待是否超时,如果有用户按键则跳转到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 />
如果用户按下&ldquo;Enter&rdquo;键,和超时等同处理,这样,就启动缺省的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 />
如果是除&ldquo;Enter&rdquo;键外其他的键,则检查是不是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 />
判断引导扇区的最后两个字节是否是&ldquo;0x55AA&rdquo;,如果不是,则表示此扇区是不可引导的,或<br />
不合法的引导扇区则直接跳转到main.10。使用户重新选择启动分区。<br />
<br />
        pushw %si             # Save<br />
        movw $crlf,%si         # Leave some<br />
        callw puts             #  space<br />
        popw %si             # Restore<br />
<br />
打印&ldquo;回车&rdquo;和&ldquo;换行&rdquo;。<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 />
&ldquo;putkey&rdquo;函数在屏幕上打印&ldquo;F1&rdquo;、&ldquo;F2&rdquo;或&ldquo;F3&rdquo;等。如果dl为0则打印&ldquo;F1&rdquo;,<br />
如果dl为1则打印&ldquo;F2&rdquo;,如果dl为3则打印&ldquo;F3&rdquo;。和调用&ldquo;putstr&rdquo;在屏幕上打印<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: #调用&ldquo;putstr&rdquo;在屏幕上打印es:si指向的以最高位置1为结束字符的字符串。<br />
        callw putstr                 # Display string<br />
<br />
putn: #&ldquo;putn&rdquo;打印&ldquo;回车/换行&rdquo;后在屏幕上打印es:si指向的以最高位置1为结束字符的字符串。<br />
        movw $crlf,%si                 # To next line<br />
<br />
putstr: #&ldquo;putstr&rdquo;在屏幕上打印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 />
&ldquo;putchr&rdquo;在屏幕上显示&ldquo;al&rdquo;中的字符。<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>
登录后复制


最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号