; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                               boot.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                                                       Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                                                       Noted by Ethan_zhang, 2011
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

;%define    _BOOT_DEBUG_    ; 做 Boot Sector 时一定将此行注释掉!将此行打开后用 nasm Boot.asm -o Boot.com 做成一个.COM文件易于调试

%ifdef    _BOOT_DEBUG_
    org  0100h            ; 调试状态, 做成 .COM 文件, 可调试
%else
    org  07c00h            ; Boot 状态, Bios 将把 Boot Sector 加载到 0:7C00 处并开始执行
%endif

;================================================================================================
;因为堆栈是从高往底走的,而程序是从0100h或07c00h的位置开始往高地址走的,所以定义堆栈的栈顶为程序的加载地址
%ifdef    _BOOT_DEBUG_
BaseOfStack        equ    0100h    ; 调试状态下堆栈基地址(栈底, 从这个位置向低地址生长)
%else
BaseOfStack        equ    07c00h    ; Boot状态下堆栈基地址(栈底, 从这个位置向低地址生长)
%endif

%include    "load.inc"
;================================================================================================

;=================================Ethan_zhang========================================
;jmp short 段内短转移,以当前ip的相对跳转地址的偏依量作为转移量,但偏移量只允许在-128 ~ +127字节之间
;nop是fat12文件头格式中的BS_jmpBoot固定格式
;=================================Ethan_zhang========================================

jmp short LABEL_START        ; Start to boot.
    nop                ; 这个 nop 不可少

; 下面是 FAT12 磁盘的头, 之所以包含它是因为下面用到了磁盘的一些信息
%include    "fat12hdr.inc"

LABEL_START:    
    mov    ax, cs
    mov    ds, ax
    mov    es, ax
    mov    ss, ax
    mov    sp, BaseOfStack

; 清屏
    mov    ax, 0600h        ; AH = 6,  AL = 0h
    mov    bx, 0700h        ; 黑底白字(BL = 07h)
    mov    cx, 0            ; 左上角: (0, 0)
    mov    dx, 0184fh        ; 右下角: (80, 50)
    int    10h            ; int 10h

;=================================Ethan_zhang========================================
;在后面定义了一系列固定长度的字符串,所以这里只要用dh来表示要显示第几个固定长度的字符地址就可以显示字符了
;=================================Ethan_zhang========================================
    mov    dh, 0            ; "Booting  "
    call    DispStr            ; 显示字符串
    
    xor    ah, ah    ; ┓
    xor    dl, dl    ; ┣ 软驱复位
    int    13h    ; ┛

;=================================Ethan_zhang========================================
;寻找LOADER.BIN文件的大体思路为:
;LOADER.BIN文件存储在A盘中,我们知道A盘的格式是FAT12格式,具体格式见书本P103,其主要是有引导扇区、
;FAT1区、FAT2区、根目录区(长度不固定,需要计算)和 数据区组成。引导扇区放的就是我们写的引导代码boot.bin
;文件,我们的LOADER.BIN文件是放在数据区中的,而文件的文件名是放在根目录中,因此,如果我们想查看该盘是否
;含有我们想要的文件时,我们应该先到根目录中查看是否含有该文件名,如果有,说明该盘存在该文件,那么下面可以
;从盘中读取该文件了,文件在盘中是按照簇来进行存储的,而在FAT12盘中,一个簇中只含有一个扇区,故下面我们只以
;扇区来进行说明。所以文件在FAT12中是按照扇区(一个扇区512字节)来进行存储的,所以一个文件一般会占多个扇区,
;而每个文件占的扇区数会记录在FAT1区中,我们只要从文件根目录中获取该文件在FAT1区中的起始地址,只要该地址
;存储的不是0xFFF说明该文件还占有其他扇区,我们继续读取该地址指向的扇区,直到FAT1项中的值为0xFFF,这表示文件
;结束,读取完毕而根目录也是按照扇区进行存储的,所以我们得从根目录起始扇区开始顺序读取每个扇区,直到根目录区结束。
;而每个根目录项是32个字节,因此一个扇区可以有16个根目录项,我们挨个比较每个根目录项中的文件名和我们期望查找的文件名
;是否相同,如果相同就表示已经找到,只要取出根目录项中的DIR_FstClus,然后从FAT1项中取出相应的数据即可。
;如果没有找到,那么就到下一个根目录项中查找,如果当前扇区都没有找到,那么读取下一个扇区,知道这个根目录区结束。
;=================================Ethan_zhang========================================
; 下面在 A 盘的根目录寻找 LOADER.BIN
    mov    word [wSectorNo], SectorNoOfRootDirectory
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
    cmp    word [wRootDirSizeForLoop], 0    ; ┓
    jz    LABEL_NO_LOADERBIN                            ; ┣ 判断根目录区是不是已经读完
    dec    word [wRootDirSizeForLoop]            ; ┛ 如果读完表示没有找到 LOADER.BIN
    mov    ax, BaseOfLoader
    mov    es, ax            ; es <- BaseOfLoader
    mov    bx, OffsetOfLoader    ; bx <- OffsetOfLoader    于是, es:bx = BaseOfLoader:OffsetOfLoader
    mov    ax, [wSectorNo]    ; ax <- Root Directory 中的某 Sector 号
    mov    cl, 1                ; 要读取的扇区数目
    call    ReadSector    ;将cl个Sector中的数据读到内存es:bx处

mov    si, LoaderFileName    ; ds:si -> "LOADER  BIN"
    mov    di, OffsetOfLoader    ; es:di -> BaseOfLoader:0100 = BaseOfLoader*10h+100
    cld             ;清除方向标志,告诉程序si,di向前移动;std指令为设置方向,告诉程序si,di向后移动
    mov    dx, 10h                        ;10h表示的是16,因为一个扇区是512字节,而每个根目录项是32字节,
                                                    ;故每个扇区含有512/32=16个根目录项
LABEL_SEARCH_FOR_LOADERBIN:
    cmp    dx, 0                    ; ┓循环次数控制,
    jz    LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR    ; ┣如果已经读完了一个 Sector,
    dec    dx                    ; ┛就跳到下一个 Sector
    mov    cx, 11
LABEL_CMP_FILENAME:
    cmp    cx, 0
    jz    LABEL_FILENAME_FOUND    ; 如果比较了 11 个字符都相等, 表示找到
dec    cx
    lodsb                ;将 ds:si 指向的地址内容加载到 al
    cmp    al, byte [es:di]
    jz    LABEL_GO_ON
    jmp    LABEL_DIFFERENT        ; 只要发现不一样的字符就表明本 DirectoryEntry 不是
; 我们要找的 LOADER.BIN
LABEL_GO_ON:
    inc    di
    jmp    LABEL_CMP_FILENAME    ;    继续循环

LABEL_DIFFERENT:
    and    di, 0FFE0h        ;      ┓di &= E0 为了让它指向本条目开头;因为E0的二进制为1110 0000,后面五位
                                    ;       ┃刚好是32个字节,因此di与上一个OFFE0h,可以回到本条目录的开头
    add    di, 20h            ;      ┃让di增加32个字节,即一个根目录项的大小,从而进入下一个根目录项
    mov    si, LoaderFileName    ;      ┣ 让si重新指向LoaderFileName的起始位置,因为在前面的比较过程中
                                                    ;       ┃si移动了,要重新比较的话,还得重新回到起始点
    jmp    LABEL_SEARCH_FOR_LOADERBIN;    ┛

LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
    add    word [wSectorNo], 1
    jmp    LABEL_SEARCH_IN_ROOT_DIR_BEGIN

LABEL_NO_LOADERBIN:
    mov    dh, 2            ; "No LOADER."
    call    DispStr            ; 显示字符串
%ifdef    _BOOT_DEBUG_
    mov    ax, 4c00h        ; ┓
    int    21h            ; ┛没有找到 LOADER.BIN, 回到 DOS,21号中断是回到DOS中
%else
    jmp    $            ; 没有找到 LOADER.BIN, 死循环在这里
%endif

LABEL_FILENAME_FOUND:            ; 找到 LOADER.BIN 后便来到这里继续
    mov    ax, RootDirSectors
    and    di, 0FFE0h        ; 同上面一样,回到当前根目录项的起始位置
    add    di, 01Ah        ; 01Ah是DIR_FstClus 在根目录项中的偏移地址,它表示该文件在FAT1区中的起始序号
    mov    cx, word [es:di]
    push    cx            ; 保存此 Sector 在 FAT 中的序号
    add    cx, ax
    add    cx, DeltaSectorNo    ; 这句完成时 cl 里面变成 LOADER.BIN 的起始扇区号 (从 0 开始数的序号)
    mov    ax, BaseOfLoader
    mov    es, ax            ; es <- BaseOfLoader
    mov    bx, OffsetOfLoader    ; bx <- OffsetOfLoader    于是, es:bx = BaseOfLoader:OffsetOfLoader = BaseOfLoader * 10h + OffsetOfLoader
    mov    ax, cx            ; ax <- Sector 号

LABEL_GOON_LOADING_FILE:
    push    ax            ; ┓
    push    bx            ; ┃
    mov    ah, 0Eh    ; ┃ 每读一个扇区就在 "Booting  " 后面打一个点, 形成这样的效果:
    mov    al, '.'        ; ┃
    mov    bl, 0Fh    ; ┃ Booting ......
    int    10h                ; ┃ 10号中断是专门用来显示字符用的,其中AH表示要显示字符的格式
    pop    bx                ; ┃ AL表示要显示的字符,所以显示一个字符占2个字节
    pop    ax                ; ┛

mov    cl, 1
    call    ReadSector
    pop    ax            ; 取出此 Sector 在 FAT 中的序号
    call    GetFATEntry
    cmp    ax, 0FFFh
    jz    LABEL_FILE_LOADED
    push    ax            ; 保存 Sector 在 FAT 中的序号
    mov    dx, RootDirSectors
    add    ax, dx
    add    ax, DeltaSectorNo
    add    bx, [BPB_BytsPerSec]    ;BPB_BytsPerSec表示每个扇区的字节数,即512字节
                                                    ;将其赋给bx,说明将bx后移512个字节,Sector->es:bx
    jmp    LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:

mov    dh, 1            ; "Ready."
    call    DispStr            ; 显示字符串

; *****************************************************************************************************
    jmp    BaseOfLoader:OffsetOfLoader    ; 这一句正式跳转到已加载到内存中的 LOADER.BIN 的开始处
                        ; 开始执行 LOADER.BIN 的代码
                        ; Boot Sector 的使命到此结束
; *****************************************************************************************************

;============================================================================
;变量
;----------------------------------------------------------------------------
wRootDirSizeForLoop    dw    RootDirSectors    ; Root Directory 占用的扇区数, 在循环中会递减至零.
wSectorNo        dw    0        ; 要读取的扇区号
bOdd            db    0        ; 奇数还是偶数

;============================================================================
;字符串
;----------------------------------------------------------------------------
LoaderFileName        db    "LOADER  BIN", 0    ; LOADER.BIN 之文件名
; 为简化代码, 下面每个字符串的长度均为 MessageLength
MessageLength        equ    9
BootMessage:        db    "Booting  "; 9字节, 不够则用空格补齐. 序号 0
Message1        db    "Ready.   "; 9字节, 不够则用空格补齐. 序号 1
Message2        db    "No LOADER"; 9字节, 不够则用空格补齐. 序号 2
;============================================================================

;----------------------------------------------------------------------------
; 函数名: DispStr
;----------------------------------------------------------------------------
; 作用:
;    显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)
DispStr:
    mov    ax, MessageLength
    mul    dh
    add    ax, BootMessage
    mov    bp, ax            ; ┓
    mov    ax, ds            ; ┣ ES:BP = 串地址
    mov    es, ax            ; ┛
    mov    cx, MessageLength    ; CX = 串长度
    mov    ax, 01301h        ; AH = 13,  AL = 01h
    mov    bx, 0007h        ; 页号为0(BH = 0) 黑底白字(BL = 07h)
    mov    dl, 0
    int    10h            ; 调用10号中断显示字符
    ret

;----------------------------------------------------------------------------
; 函数名: ReadSector
;----------------------------------------------------------------------------
; 作用:
;    从第 ax 个 Sector 开始, 将 cl 个 Sector 读入 es:bx 中
ReadSector:
    ; -----------------------------------------------------------------------
    ; 怎样由扇区号求扇区在磁盘中的位置 (扇区号 -> 柱面号, 起始扇区, 磁头号)
    ; -----------------------------------------------------------------------
    ; 设扇区号为 x
    ;                              ┌ 柱面号 = y >> 1
    ;       x                       ┌ 商 y ┤
    ; -------------- => ┤      └ 磁头号 = y & 1
    ;  每磁道扇区数     │
    ;                              └ 余 z => 起始扇区号 = z + 1
    push    bp
    mov    bp, sp
    sub    esp, 2            ; 辟出两个字节的堆栈区域保存要读的扇区数: byte [bp-2]

mov    byte [bp-2], cl
    push    bx            ; 保存 bx
    mov    bl, [BPB_SecPerTrk]    ; bl: 除数
    div    bl            ; y 在 al 中, z 在 ah 中
    inc    ah            ; z ++
    mov    cl, ah            ; cl <- 起始扇区号
    mov    dh, al            ; dh <- y
    shr    al, 1            ; y >> 1 (其实是 y/BPB_NumHeads, 这里BPB_NumHeads=2)
    mov    ch, al            ; ch <- 柱面号
    and    dh, 1            ; dh & 1 = 磁头号
    pop    bx            ; 恢复 bx
    ; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^
    mov    dl, [BS_DrvNum]        ; 驱动器号 (0 表示 A 盘)
.GoOnReading:
    mov    ah, 2            ; 读
    mov    al, byte [bp-2]        ; 读 al 个扇区
    int    13h
    jc    .GoOnReading        ; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到正确为止

add    esp, 2
    pop    bp

ret

;----------------------------------------------------------------------------
; 函数名: GetFATEntry
;----------------------------------------------------------------------------
; 作用:
;    找到序号为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中
;    需要注意的是, 中间需要读 FAT 的扇区到 es:bx 处, 所以函数一开始保存了 es 和 bx
GetFATEntry:
    push    es
    push    bx
    push    ax
    mov    ax, BaseOfLoader    ; ┓
    sub    ax, 0100h                    ; ┣ 在 BaseOfLoader 后面留出 4K 空间用于存放 FAT
    mov    es, ax                        ; ┛
    pop    ax
    mov    byte [bOdd], 0
    mov    bx, 3
    mul    bx            ; dx:ax = ax * 3
    mov    bx, 2
    div    bx            ; dx:ax / 2  ==>  ax <- 商, dx <- 余数
    cmp    dx, 0
    jz    LABEL_EVEN
    mov    byte [bOdd], 1
LABEL_EVEN:;偶数
    xor    dx, dx            ; 现在 ax 中是 FATEntry 在 FAT 中的偏移量. 下面来计算 FATEntry 在哪个扇区中(FAT占用不止一个扇区)
    mov    bx, [BPB_BytsPerSec]
    div    bx            ; dx:ax / BPB_BytsPerSec  ==>    ax <- 商   (FATEntry 所在的扇区相对于 FAT 来说的扇区号)
                    ;                dx <- 余数 (FATEntry 在扇区内的偏移)。
    push    dx
    mov    bx, 0            ; bx <- 0    于是, es:bx = (BaseOfLoader - 100):00 = (BaseOfLoader - 100) * 10h
    add    ax, SectorNoOfFAT1    ; 此句执行之后的 ax 就是 FATEntry 所在的扇区号
    mov    cl, 2
    call    ReadSector        ; 读取 FATEntry 所在的扇区, 一次读两个, 避免在边界发生错误, 因为一个 FATEntry 可能跨越两个扇区
    pop    dx
    add    bx, dx
    mov    ax, [es:bx]
    cmp    byte [bOdd], 1
    jnz    LABEL_EVEN_2
    shr    ax, 4
LABEL_EVEN_2:
    and    ax, 0FFFh

LABEL_GET_FAT_ENRY_OK:

pop    bx
    pop    es
    ret
;----------------------------------------------------------------------------

times     510-($-$$)    db    0    ; 填充剩下的空间,使生成的二进制代码恰好为512字节
dw     0xaa55                ; 结束标志

boot.asm文件注释相关推荐

  1. Spring boot yml文件的书写格式

    Spring boot yml文件的书写格式 使用ide 创建好spring boot文件格式后https://blog.csdn.net/weixin_42292697/article/detail ...

  2. 项目_功能模块_基于Spring Boot的文件上传下载功能的设计与实现

    文章目录 基于Spring Boot的文件上传下载功能模块的设计与实现 1.前言 2.技术栈 3.关键源码 4.实现效果 4.1.登录 4.2.文件列表 4.3.上传文件测试 4.3.1.测试图片 4 ...

  3. VsCode允许JSON文件注释

    打开设置面板 输入Files: Associations搜索出此项 在此处添加项: 项 *.json 值 jsonc 然后你的JSON文件注释就不报错了~

  4. 双系统启动boot.ini文件的秘密

    Windows NT类的操作系统,也就是Windows NT/2000/XP中,有一个特殊文件,也就是"BOOT.INI"文件,这个文件会很轻松地按照我们的需求设置好多重启动系统. ...

  5. pycharm python脚本自动头文件注释

    pycharm python脚本自动头文件注释 用的脚本 #!/usr/bin/env python # -*- encoding: utf-8 -*- """ @Int ...

  6. 全志H3 uboot传参到内核分析,boot.scr文件分析

    传参过程 uboot倒计时结束,默认以bootcmd来启动内核,它等于 fatload mmc 0:1 ${scriptaddr} boot.scr; source ${scriptaddr} 上面两 ...

  7. zend studio自动添加文件注释和方法注释

    zend studio自动添加文件注释和方法注释 进入首选项=>PHP=>Editor=>Template=>New Name\Description\Pattern里面分别填 ...

  8. Windows内核启动开关/3GB和win10下boot.ini文件问题

    在Windows中,用户代码和内核代码有各自的运行环境,它们可以访问的内存空间不同.32位系统中,内核代码可以访问当前进程的整个4GB虚拟地址空间,用户代码只能访问底端的2GB虚拟地址(或3GB,如果 ...

  9. pycharm 自动生成文件注释和函数注释

    1. 文件注释 pycharm提供了一个在新建文件自动生成文件头注释的功能,可以实现自动生成运行环境,作者.日期等必要信息,使用比较方便,配置十分简单.在anaconda 的spider也有类似的功能 ...

最新文章

  1. Gradle dependency cache may be corrupt
  2. Centos双节点搭建openstack无法使用ssh链接到实例解决办法
  3. Directory lookup for the file xxx.mdf failed with the operating system error 2
  4. 讲解Guitar Pro打谱使用技巧
  5. msp430入门学习30
  6. swift编写命令行工具
  7. POSIX:可移植操作系统接口(Portable Operating System Interface of UNIX,缩写为 POSIX )
  8. 检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件时失败,原因是出现以下错误: 80070005。...
  9. python100例详解-Python类和实例详解
  10. Java Thread等待,通知和notifyAll示例
  11. 揭密征途赚钱模式:史玉柱在赚谁的钱
  12. 几款实用的VUE移动端UI框架
  13. unity3d 取锚点位置_加热炉传输点
  14. 简单易懂的 全景图高清下载方法以及原理简要解析(支持下载建E、720yun、酷雷曼、景站、酷家乐、百度街景原图)
  15. SCI检索号识别一法:UT-WOS与UT-ISI
  16. vue文件下载及重命名
  17. flutter在IOS上的登录实现——QQ登录、微信登录、自动识别手机号一键登录、apple登录
  18. C#按钮添加图片的解决
  19. 如何用PDF编辑器编辑、修改PDF文本和图片?
  20. 索尼和微软同时发布新游戏机——抢钱了

热门文章

  1. POJ3498最大流,枚举终点,企鹅,基础最大流
  2. 汇编 头文件 依赖库文件
  3. 【集合论】二元关系 ( 二元关系运算示例 | 逆运算示例 | 合成运算示例 | 限制运算示例 | 像运算示例 )
  4. 【Android RTMP】RTMPDump 封装 RTMPPacket 数据包 ( 关键帧数据格式 | 非关键帧数据格式 | x264 编码后的数据处理 | 封装 H.264 视频数据帧 )
  5. Dart 调用C语言混合编程
  6. centos7.3安装MongoDB
  7. 【转】解决从jenkins打开robot framework报告会提示‘Opening Robot Framework log failed ’的问题...
  8. 在eclipse中创建web项目(非myeclipse)
  9. SAP中有关差异的一些概念
  10. ROS学习(八):ROS URDF-transmission