• 第四章boot.asm


    跟上篇日志中间又隔了好几天,懒惰啊

     1 寻找LOADER.BIN文件的大体思路为:
     2 LOADER.BIN文件存储在A盘中,我们知道A盘的格式是FAT12格式,具体格式见书本P103,其主要是有引导扇区
     3 、FAT1区、FAT2区、根目录区(长度不固定,需要计算)和 数据区组成。引导扇区放的就是我们写的引导代码
     4 boot.bin文件,我们的LOADER.BIN文件是放在数据区中的,而文件的文件名是放在根目录中,因此,如果我
     5 们想查看该盘是否含有我们想要的文件时,我们应该先到根目录中查看是否含有该文件名,如果有,说明该盘存
     6 在该文件,那么下面可以从盘中读取该文件了。
     7 文件在盘中是按照簇来进行存储的,而在FAT12盘中,一个簇中只含有一个扇区,故下面我们只以扇区来进行说
     8 明。所以文件在FAT12中是按照扇区(一个扇区512字节)来进行存储的,所以一个文件一般会占多个扇区,而
     9 每个文件占的扇区数会记录在FAT1区中,我们只要从文件根目录中获取该文件在FAT1区中的起始地址,只要该
    10 地址存储的不是0xFFF说明该文件还占有其他扇区,我们继续读取该地址指向的扇区,直到FAT1项中的值为
    11 0xFFF,这表示文件结束,读取完毕.而根目录也是按照扇区进行存储的,所以我们得从根目录起始扇区开始顺
    12 序读取每个扇区,直到根目录区结束。而每个根目录项是32个字节,因此一个扇区可以有16个根目录项,我们
    13 挨个比较每个根目录项中的文件名和我们期望查找的文件名是否相同,如果相同就表示已经找到,只要取出根目录项中的DIR_FstClus,然后从FAT1项中取出相应的数据即可。
    14 如果没有找到,那么就到下一个根目录项中查找,如果当前扇区都没有找到,那么读取下一个扇区,直到这个根;目录区结束。

    寻找loader.bin的思路

    1.首先要明白loader.bin文件的数据部分放在数据区,而关于文件大小,文件名等文件信息放在根目录区

    2.所以找文件的时候先到根目录区找到文件名为loader.bin的文件信息,怎么找呢?我们知道,每个文件的信息在根目录中占一项,每项为32字节,每个扇区共有16项;同时我们

    还知道根目录开始扇区位19,所以我们从该扇区开始读取数据,每次读一个扇区。在度过一个扇区以后,我们要分析这个扇区中的内容,具体是比较目录项中文件名是否为

    loader.bin,若是则在根目录中文件查找完毕

    3.在根目录中找到loader.bin的信息之后,我们可以得到该文件在数据区的簇号(扇区号),同时也得到在fat表中的对应的项(index),p131小例子

    4.反复根据步骤三得到信息读完整个文件

      1 ;%define    _BOOT_DEBUG_    ; 做 Boot Sector 时一定将此行注释掉!将此行打开后用 nasm Boot.asm -o Boot.com 做成一个.COM文件易于调试
      2 
      3 %ifdef    _BOOT_DEBUG_
      4     org  0100h            ; 调试状态, 做成 .COM 文件, 可调试
      5 %else
      6     org  07c00h            ; Boot 状态, Bios 将把 Boot Sector 加载到 0:7C00 处并开始执行
      7 %endif
      8 
      9 ;================================================================================================
     10 %ifdef    _BOOT_DEBUG_
     11 BaseOfStack        equ    0100h    ; 调试状态下堆栈基地址(栈底, 从这个位置向低地址生长)
     12 %else
     13 BaseOfStack        equ    07c00h    ; Boot状态下堆栈基地址(栈底, 从这个位置向低地址生长)
     14 %endif
     15 
     16 BaseOfLoader        equ    09000h    ; LOADER.BIN 被加载到的位置 ----  段地址
     17 OffsetOfLoader        equ    0100h    ; LOADER.BIN 被加载到的位置 ---- 偏移地址
     18 
     19 RootDirSectors        equ    14    ; 根目录占用空间
     20 SectorNoOfRootDirectory    equ    19    ; Root Directory 的第一个扇区号
     21 SectorNoOfFAT1        equ    1    ; FAT1 的第一个扇区号    = BPB_RsvdSecCnt
     22 DeltaSectorNo        equ    17    ; DeltaSectorNo = BPB_RsvdSecCnt + (BPB_NumFATs * FATSz) - 2
     23                     ; 文件的开始Sector号 = DirEntry中的开始Sector号 + 根目录占用Sector数目 + DeltaSectorNo
     24 ;================================================================================================
     25 
     26     jmp short LABEL_START        ; Start to boot.
     27     nop                ; 这个 nop 不可少
     28 
     29     ; 下面是 FAT12 磁盘的头
     30     BS_OEMName    DB 'ForrestY'    ; OEM String, 必须 8 个字节
     31     BPB_BytsPerSec    DW 512        ; 每扇区字节数
     32     BPB_SecPerClus    DB 1        ; 每簇多少扇区
     33     BPB_RsvdSecCnt    DW 1        ; Boot 记录占用多少扇区
     34     BPB_NumFATs    DB 2        ; 共有多少 FAT 表
     35     BPB_RootEntCnt    DW 224        ; 根目录文件数最大值
     36     BPB_TotSec16    DW 2880        ; 逻辑扇区总数
     37     BPB_Media    DB 0xF0        ; 媒体描述符
     38     BPB_FATSz16    DW 9        ; 每FAT扇区数
     39     BPB_SecPerTrk    DW 18        ; 每磁道扇区数
     40     BPB_NumHeads    DW 2        ; 磁头数(面数)
     41     BPB_HiddSec    DD 0        ; 隐藏扇区数
     42     BPB_TotSec32    DD 0        ; 如果 wTotalSectorCount 是 0 由这个值记录扇区数
     43     BS_DrvNum    DB 0        ; 中断 13 的驱动器号
     44     BS_Reserved1    DB 0        ; 未使用
     45     BS_BootSig    DB 29h        ; 扩展引导标记 (29h)
     46     BS_VolID    DD 0        ; 卷序列号
     47     BS_VolLab    DB 'Tinix0.01  '; 卷标, 必须 11 个字节
     48     BS_FileSysType    DB 'FAT12   '    ; 文件系统类型, 必须 8个字节  
     49 
     50 LABEL_START:    
     51     mov    ax, cs
     52     mov    ds, ax
     53     mov    es, ax
     54     mov    ss, ax
     55     mov    sp, BaseOfStack
     56 
     57     ; 清屏
     58     mov    ax, 0600h        ; AH = 6,  AL = 0h
     59     mov    bx, 0700h        ; 黑底白字(BL = 07h)
     60     mov    cx, 0            ; 左上角: (0, 0)
     61     mov    dx, 0184fh        ; 右下角: (80, 50)
     62     int    10h            ; int 10h
     63 
     64     mov    dh, 0            ; "Booting  "
     65     call    DispStr            ; 显示字符串
     66     
     67     xor    ah, ah    ;
     68     xor    dl, dl    ; ┣ 软驱复位
     69     int    13h    ;
     70     
     71 ; 下面在 A 盘的根目录寻找 LOADER.BIN
     72     mov    word [wSectorNo], SectorNoOfRootDirectory
     73 LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
     74     cmp    word [wRootDirSizeForLoop], 0    ;
     75     jz    LABEL_NO_LOADERBIN        ; ┣ 判断根目录区是不是已经读完
     76     dec    word [wRootDirSizeForLoop]    ; ┛ 如果读完表示没有找到 LOADER.BIN
     77     mov    ax, BaseOfLoader
     78     mov    es, ax            ; es <- BaseOfLoader
     79     mov    bx, OffsetOfLoader    ; bx <- OffsetOfLoader    于是, es:bx = BaseOfLoader:OffsetOfLoader
     80     mov    ax, [wSectorNo]    ; ax <- Root Directory 中的某 Sector 号
     81     mov    cl, 1
     82     call    ReadSector
     83 
     84     mov    si, LoaderFileName    ; ds:si -> "LOADER  BIN"
     85     mov    di, OffsetOfLoader    ; es:di -> BaseOfLoader:0100 = BaseOfLoader*10h+100
     86     cld
     87     mov    dx, 10h ;这里为什么是16,因为目录项中每项为32字节,每个扇区为32字节,所以共有16项
     88 LABEL_SEARCH_FOR_LOADERBIN:
     89     cmp    dx, 0                                        ; ┓循环次数控制,
     90     jz    LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR    ; ┣如果已经读完了一个 Sector,
     91     dec    dx                                            ; ┛就跳到下一个 Sector
     92     mov    cx, 11                                      ;11个字符,有一个不一样则不是所要找的文件
     93 LABEL_CMP_FILENAME:
     94     cmp    cx, 0
     95     jz    LABEL_FILENAME_FOUND    ; 如果比较了 11 个字符都相等, 表示找到
     96 dec    cx
     97     lodsb                ; ds:si -> al
     98     cmp    al, byte [es:di]
     99     jz    LABEL_GO_ON
    100     jmp    LABEL_DIFFERENT        ; 只要发现不一样的字符就表明本 DirectoryEntry 不是
    101 ; 我们要找的 LOADER.BIN
    102 LABEL_GO_ON:
    103     inc    di
    104     jmp    LABEL_CMP_FILENAME    ;    继续循环
    105 
    106 LABEL_DIFFERENT:
    107     and    di, 0FFE0h                        ; else ┓    di &= E0 为了让它指向本条目开头
    108     add    di, 20h                            ;
    109     mov    si, LoaderFileName                    ;     ┣ di += 20h  下一个目录条目
    110     jmp    LABEL_SEARCH_FOR_LOADERBIN;
    111 
    112 LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
    113     add    word [wSectorNo], 1
    114     jmp    LABEL_SEARCH_IN_ROOT_DIR_BEGIN
    115 
    116 LABEL_NO_LOADERBIN:
    117     mov    dh, 2            ; "No LOADER."
    118     call    DispStr            ; 显示字符串
    119 %ifdef    _BOOT_DEBUG_
    120     mov    ax, 4c00h        ;
    121     int    21h            ; ┛没有找到 LOADER.BIN, 回到 DOS
    122 %else
    123     jmp    $            ; 没有找到 LOADER.BIN, 死循环在这里
    124 %endif
    125 
    126 LABEL_FILENAME_FOUND:            ; 找到 LOADER.BIN 后便来到这里继续
    127     mov    ax, RootDirSectors
    128     and    di, 0FFE0h        ; di -> 当前条目的开始
    129     add    di, 01Ah        ; di -> 首 Sector,该条目对应的开始簇号(扇区号),看根目录条目格式表
    130     mov    cx, word [es:di]
    131     push    cx            ; 保存此 Sector 在 FAT 中的序号
    132     add    cx, ax
    133     add    cx, DeltaSectorNo    ; 这句完成时 cl 里面变成 LOADER.BIN 的起始扇区号 (从 0 开始数的序号)
    134     mov    ax, BaseOfLoader
    135     mov    es, ax            ; es <- BaseOfLoader
    136     mov    bx, OffsetOfLoader    ; bx <- OffsetOfLoader    于是, es:bx = BaseOfLoader:OffsetOfLoader = BaseOfLoader * 10h + OffsetOfLoader
    137     mov    ax, cx            ; ax <- Sector 号
    138 
    139 LABEL_GOON_LOADING_FILE:
    140     push    ax            ;
    141     push    bx            ;
    142     mov    ah, 0Eh            ; ┃ 每读一个扇区就在 "Booting  " 后面打一个点, 形成这样的效果:
    143     mov    al, '.'            ;
    144     mov    bl, 0Fh            ; ┃ Booting ......
    145     int    10h            ; ┃AL=字符,BH=页码,BL=颜色(只适用于图形模式)
    146     pop    bx            ;
    147     pop    ax            ;
    148 
    149     mov    cl, 1
    150     call    ReadSector
    151     pop    ax            ; 取出此 Sector 在 FAT 中的序号,见132行
    152     call    GetFATEntry
    153     cmp    ax, 0FFFh
    154     jz    LABEL_FILE_LOADED
    155     push    ax            ; 保存 Sector 在 FAT 中的序号
    156     mov    dx, RootDirSectors
    157     add    ax, dx
    158     add    ax, DeltaSectorNo
    159     add    bx, [BPB_BytsPerSec]
    160     jmp    LABEL_GOON_LOADING_FILE
    161 LABEL_FILE_LOADED:
    162 
    163     mov    dh, 1            ; "Ready."
    164     call    DispStr            ; 显示字符串
    165 
    166 ; *****************************************************************************************************
    167     jmp    BaseOfLoader:OffsetOfLoader    ; 这一句正式跳转到已加载到内存中的 LOADER.BIN 的开始处
    168                         ; 开始执行 LOADER.BIN 的代码
    169                         ; Boot Sector 的使命到此结束
    170 ; *****************************************************************************************************
    171 
    172 
    173 
    174 ;============================================================================
    175 ;变量
    176 ;----------------------------------------------------------------------------
    177 wRootDirSizeForLoop    dw    RootDirSectors    ; Root Directory 占用的扇区数, 在循环中会递减至零.
    178 wSectorNo        dw    0        ; 要读取的扇区号
    179 bOdd            db    0        ; 奇数还是偶数
    180 
    181 ;============================================================================
    182 ;字符串
    183 ;----------------------------------------------------------------------------
    184 LoaderFileName        db    "LOADER  BIN", 0    ; LOADER.BIN 之文件名
    185 ; 为简化代码, 下面每个字符串的长度均为 MessageLength
    186 MessageLength        equ    9
    187 BootMessage:        db    "Booting  "; 9字节, 不够则用空格补齐. 序号 0
    188 Message1        db    "Ready.   "; 9字节, 不够则用空格补齐. 序号 1
    189 Message2        db    "No LOADER"; 9字节, 不够则用空格补齐. 序号 2
    190 ;============================================================================
    191 
    192 
    193 ;----------------------------------------------------------------------------
    194 ; 函数名: DispStr
    195 ;----------------------------------------------------------------------------
    196 ; 作用:
    197 ;    显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)
    198 DispStr:
    199     mov    ax, MessageLength
    200     mul    dh
    201     add    ax, BootMessage
    202     mov    bp, ax            ;
    203     mov    ax, ds            ; ┣ ES:BP = 串地址
    204     mov    es, ax            ;
    205     mov    cx, MessageLength    ; CX = 串长度
    206     mov    ax, 01301h        ; AH = 13,  AL = 01h
    207     mov    bx, 0007h        ; 页号为0(BH = 0) 黑底白字(BL = 07h)
    208     mov    dl, 0
    209     int    10h            ; int 10h
    210     ret
    211 
    212 
    213 ;----------------------------------------------------------------------------
    214 ; 函数名: ReadSector
    215 ;----------------------------------------------------------------------------
    216 ; 作用:
    217 ;    从第 ax 个 Sector 开始, 将 cl 个 Sector 读入 es:bx 中
    218 ReadSector:
    219     ; -----------------------------------------------------------------------
    220     ; 怎样由扇区号求扇区在磁盘中的位置 (扇区号 -> 柱面号, 起始扇区, 磁头号)
    221     ; -----------------------------------------------------------------------
    222     ; 设扇区号为 x
    223     ;                           ┌ 柱面号 = y >> 1
    224     ;       x           ┌ 商 y ┤
    225     ; -------------- => ┤      └ 磁头号 = y & 1
    226     ;  每磁道扇区数     │
    227     ;                   └ 余 z => 起始扇区号 = z + 1
    228     push    bp
    229     mov    bp, sp
    230     sub    esp, 2            ; 辟出两个字节的堆栈区域保存要读的扇区数: byte [bp-2]
    231 
    232     mov    byte [bp-2], cl
    233     push    bx            ; 保存 bx
    234     mov    bl, [BPB_SecPerTrk]    ; bl: 除数
    235     div    bl            ; y 在 al 中, z 在 ah 中
    236     inc    ah            ; z ++
    237     mov    cl, ah            ; cl <- 起始扇区号
    238     mov    dh, al            ; dh <- y
    239     shr    al, 1            ; y >> 1 (其实是 y/BPB_NumHeads, 这里BPB_NumHeads=2)
    240     mov    ch, al            ; ch <- 柱面号
    241     and    dh, 1            ; dh & 1 = 磁头号
    242     pop    bx            ; 恢复 bx
    243     ; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^
    244     mov    dl, [BS_DrvNum]        ; 驱动器号 (0 表示 A 盘)
    245 .GoOnReading:
    246     mov    ah, 2            ;
    247     mov    al, byte [bp-2]        ; 读 al 个扇区
    248     int    13h
    249     jc    .GoOnReading        ; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到正确为止
    250 
    251     add    esp, 2
    252     pop    bp
    253 
    254     ret
    255 
    256 ;----------------------------------------------------------------------------
    257 ; 函数名: GetFATEntry
    258 ;----------------------------------------------------------------------------
    259 ; 作用:
    260 ;    找到序号为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中
    261 ;    需要注意的是, 中间需要读 FAT 的扇区到 es:bx 处, 所以函数一开始保存了 es 和 bx
    262 GetFATEntry:
    263     push    es
    264     push    bx
    265     push    ax
    266     mov    ax, BaseOfLoader    ;
    267     sub    ax, 0100h        ; ┣ 在 BaseOfLoader 后面留出 4K 空间用于存放 FAT,注意这里是0100,而不是01000
    268     mov    es, ax            ;
    269     pop    ax
    270     mov    byte [bOdd], 0
    271     mov    bx, 3
    272     mul    bx            ; dx:ax = ax * 3
    273     mov    bx, 2
    274     div    bx            ; dx:ax / 2  ==>  ax <- 商, dx <- 余数
    275     cmp    dx, 0
    276     jz    LABEL_EVEN
    277     mov    byte [bOdd], 1
    278 LABEL_EVEN:;偶数
    279     xor    dx, dx            ; 现在 ax 中是 FATEntry 在 FAT 中的偏移量. 下面来计算 FATEntry 在哪个扇区中(FAT占用不止一个扇区)
    280     mov    bx, [BPB_BytsPerSec]
    281     div    bx            ; dx:ax / BPB_BytsPerSec  ==>    ax <- 商   (FATEntry 所在的扇区相对于 FAT 来说的扇区号)
    282                     ;                dx <- 余数 (FATEntry 在扇区内的偏移)。
    283     push    dx
    284     mov    bx, 0            ; bx <- 0    于是, es:bx = (BaseOfLoader - 100):00 = (BaseOfLoader - 100) * 10h
    285     add    ax, SectorNoOfFAT1    ; 此句执行之后的 ax 就是 FATEntry 所在的扇区号
    286     mov    cl, 2
    287     call    ReadSector        ; 读取 FATEntry 所在的扇区, 一次读两个, 避免在边界发生错误, 因为一个 FATEntry 可能跨越两个扇区
    288     pop    dx
    289     add    bx, dx
    290     mov    ax, [es:bx]
    291     cmp    byte [bOdd], 1
    292     jnz    LABEL_EVEN_2
    293     shr    ax, 4
    294 LABEL_EVEN_2:
    295     and    ax, 0FFFh
    296 
    297 LABEL_GET_FAT_ENRY_OK:
    298 
    299     pop    bx
    300     pop    es
    301     ret
    302 ;----------------------------------------------------------------------------
    303 
    304 times     510-($-$$)    db    0    ; 填充剩下的空间,使生成的二进制代码恰好为512字节
    305 dw     0xaa55                ; 结束标志

    参考:

    http://blog.chinaunix.net/uid-27024249-id-3449728.html

    http://blog.csdn.net/robbie1314/article/details/5765117

    http://blog.csdn.net/asd8182651/article/details/7388541

  • 相关阅读:
    flutter 屏幕宽高 状态栏高度
    flutter 图片圆角
    flutter ListView嵌套高度问题
    Dubbo原码解析(version:2.5.3)
    ms
    InnoDB锁问题 & DB事务隔离级别
    Spring父容器与子容器
    Spring bean 的加载过程和生命周期
    logback
    Disconf (version : 2.6.21)
  • 原文地址:https://www.cnblogs.com/cdwodm/p/2912831.html
Copyright © 2020-2023  润新知