书·纪

《一个64位操作系统的实现》代码3-2 Boot和Loader加载程序

由于对intel体系结构的不熟悉……导致我在接下来的一章里前进得相当困难。遂采取最笨的方法,把代码3-2的新内容的每一条汇编逐一加上注释,尽可能去理解新的内容。收获当然是有的,但是我很怀疑是否有能力应付后面的内容。

软盘读取模块

boot启动后,要去搜索根目录中的loader.bin程序,本质上是搜索根目录部分中的loader.bin目录项,搜索到后读取目录项中记录的簇号,但是这个簇号只是起始簇号,因为loader可能会占用1个以上的簇,所以还要根据这个簇号到FAT表中去查找下一个簇号。然后根据每一次读到的簇号,最终将整个loader加载到内存中。

书中对读取软盘的功能做了封装,因为BIOS中断提供的读取功能只接受CHS (Cylinder/Head/Sector,柱面/磁头/扇区),而我们所用的是LBA(逻辑区块地址)是扇区的形式,所以将这个转换过程包装在Func_ReadeOneSector里。

以下内容摘自书中对BIOS磁盘读取功能的描述

INT 13h,AH=02h 功能:读取磁盘扇区。
AL=读入的扇区数(必须非0);
CH=磁道号(柱面号)的低8位;
CL=扇区号1~63(bit 0~5),磁道号(柱面号)的高2位(bit 6~7, 只对硬盘有效);
DH=磁头号;
DL=驱动器号(如果操作的是硬盘驱动器,bit 7必须被置位);
ES:BX=>数据缓冲区。

LBA到CHS的转换公式:

\rm LBA扇区号 \div 每磁道扇区数 = \left\{
\begin{aligned}
商Q \rightarrow \left\{
    \begin{aligned}
    柱面号 & = Q  >> 1 \\
    磁头号 & = Q \ \&  \ 1
    \end{aligned}
    \right.\\
余数R \rightarrow 起始扇区号 = \rm R + 1
\end{aligned}
\right.
;=======    read one sector from floppy
; 软盘读取功能
Func_ReadOneSector:

    push    bp                      ; 栈帧寄存器压栈
    mov bp, sp                      ; 
    sub esp,    2                   ; esp - 2 在在栈中开辟两个字节的新空间
    mov byte    [bp - 2],   cl      ; 参数cl=读入扇区的数量 存栈
    push    bx                      ; BX 存栈
    ; 用LBA扇区号计算CHS (Cylinder/Head/Sector,柱面/磁头/扇区)
    mov bl, [BPB_SecPerTrk]         ; 每磁道扇区数装入bl
    ; div 指令根据除数位数选择寄存器,8位为AX, 16位DX:AX,32位EDX:EAX,
    div bl                          ; AX/BL = AL ... AH , LBA扇区号/每磁道扇区数 = 磁道号……磁道内扇区号
    inc ah                          ; 磁道内起始扇区号从1开始,LBA从0开始,所以要+1
    mov cl, ah                      ; AH移动到CL
    mov dh, al                      ; 商AL移动到DH
    shr al, 1                       ; 柱面号 = 磁道号>>1
    mov ch, al                      ; 求得的柱面号移动至CH
    and dh, 1                       ; DH(商)与1与,得到磁头号
    pop bx                          ; 弹出BX
    ; 调用BIOS中断服务程序
    mov dl, [BS_DrvNum]             ; DL参数驱动器号

Label_Go_On_Reading:
    mov ah, 2                       ; 设置中断服务程序,ah=02h,功能是读取磁盘扇区
    mov al, byte    [bp - 2]        ; 读入的扇区数,放入al, [bp-2]是之前输入的参数,指读入扇区数
    int 13h                         ; BIOS中断服务程序
    ; 中断服务程序从软盘扇区读取数据到内存中,数据读取成功时CF复位,CF=0
    ; 此时轮询CF位,直到读取完成,完成时CF=0
    jc  Label_Go_On_Reading         ; CF = 1 回到这一块代码开始

    add esp,    2                   ; 收回2个字节的栈空间
    pop bp                          ; bp出栈
    ret                             ; 返回
    ; RET
    ; 弹出栈中的保存的返回地址(之前被CALL存进去的),回到了call Func_ReadOneSector

封装之后,只需传入扇区号即可完成磁盘读取

模块Func_ReadOneSector功能:读取磁盘扇区。
AX=待读取的磁盘起始扇区号;
CL=读入的扇区数量;
ES:BX=>目标缓冲区起始地址。

文件搜索模块

因为FAT表里只描述文件由哪些簇组成,文件属性都放在目录项里。目录项整个占32Bytes,前11Bytes是文件名。

名称 偏移 长度 描述
DIR_Name 0x00 11 文件名8B,扩展名3B
DIR_Attr 0x0B 1 文件属性
保留 0x0c 10 保留位
DIR_WrtTime 0x16 2 最后一次写入时间
DIR_WrtDate 0x18 2 最后一次写入日期
DIR_FstClus 0x1A 2 起始簇号
DIR_FileSize 0x1C 4 文件大小

下面分析一下比较文件名的过程

首先有两个字符串要比较,一个是在代码中定义的LoaderFileName,这是我们要找的文件名。另一个是读取到的目录项中的文件名,在书中的代码里,目录项被读取到了8000h这个地址

因为要比较的是字符串,每次都只能比较一个字符,所以需要两个指针,一个指向[LoaderFileName],一个指向目录项的文件名部分。每次比较一个字符,如果相同就移动指针前往下一个字符。在这里,LoaderFileName的指针是SI,目录项文件名的指针是DI

    mov si, LoaderFileName                       ; loader文件名地址装进SI
    mov di, 8000h                                ; DI = 8000H

指针的操作。对SI指针的操作,书中使用了LODSB/LODSW/LODSD/LODSQ(区别是值的位数)指令,

从DS:(R|E)SI寄存器指定的内存地址中读取数据到AL/AX/EAX/RAX寄存器
当数据载入到AL/AX/EAX/RAX寄存器后,(R|E)SI寄存器将会依据R|EFLAGS标志寄存器的DF位(之前用CLD清零的(DF=0))
自动减少或增加载入的数据长度
若DF=0, (R|E)SI++,在本程序中即指向文件名下一个字符的地址

LODSB/LODSW/LODSD/LODSQ指令可以完成:从内存([DS:(R|E)SI])读入数据到寄存器(AL/AX/EAX/RAX,取决于数据长度)-> 移动SI指针(方向取决于DF标志位)

DI指针的操作,则在比较一次后使用inc递增。

Label_Go_On:

    inc di                                       ; DI++, 移动指向目录项文件名的指针,指向下一个字符
    jmp Label_Cmp_FileName                       ; 比较这个字符

比较两个字符:

    cmp al, byte    [es:di]                      ; AL已经存入了SI指向的字符(AL是我们给出的文件名的一个字符),现在与[es:di]比较,[es:di]是从磁盘中读取到的目录的文件名部分的一个字符的值

FAT表项解析模块

因为书中使用的是FAT12文件系统,这个文件系统比较棘手的一点是一个目录项占12bit,也就是一个半Byte,首先要计算表项的存放情况,确定是先一个Byte还是先半个Byte,然后在读取表项时需要先把表项从两个字节里分离出来。

=============================
|AAAAAAAA|AAAABBBB|BBBBBBBB|
=============================
;=======    get FAT Entry

Func_GetFATEntry:
; 获取FAT表项
    push    es                      ; ES 存栈
    push    bx                      ; BX 存栈
    push    ax                      ; AX 存栈
    mov ax, 00                      ; AX = 0
    mov es, ax                      ; ES = AX = 0
    pop ax                          ; AX 出栈
    mov byte    [Odd],  0           ; 标志位Odd = 0
    mov bx, 3                       ; BX = 3
    mul bx                          ; AX = BX * AX
    mov bx, 2                       ; BX = 2
    div bx                          ; AX / BX = AX....DX  

    cmp dx, 0                       ; 比较DX,若为奇数,DX=1,若为偶数则DX=0
    jz  Label_Even                  ; 若为偶数,跳转到Label_Even,此时Odd=0
    mov byte    [Odd],  1           ; 否则标志位Odd设为1

Label_Even:

    xor dx, dx                      ; DX = DX ^ DX = 0, 这样写比mov使用更少的字节
    mov bx, [BPB_BytesPerSec]       ; BX = 每扇区字节数
    div bx                          ; AX = Fat表项偏移扇区号,DX = FAT表项在扇区中的偏移位置
    push    dx                      ; DX 存栈
    mov bx, 8000h                   ; BX = 8000H,目标缓冲区起始地址
    add ax, SectorNumOfFAT1Start    ; AX = AX + FAT1表起始扇区号(1),得到需要的FAT项所在的的扇区号,作为ReadOneSector的参数之一
    mov cl, 2                       ; CL = 2 指示ReadOneSector读入两个扇区
    call    Func_ReadOneSector      ; 调用ReadOneSector,把FAT表项所在的扇区读入到8000H

    pop dx                          ; DX 出栈
    add bx, dx                      ; BX = BX + DX,BX = 目标缓冲区起始地址 + FAT表项在扇区中的偏移位置
    mov ax, [es:bx]                 ; 读出FAT表项到AX
    cmp byte    [Odd],  1           ; Odd == 1?
    jnz Label_Even_2                ; Odd != 1 , 跳转到Label_Even_2
    shr ax, 4                       ; 奇数项右移4位(半字节),把属于前一个FAT表项的部分去掉

Label_Even_2:
    and ax, 0fffh                   ; AX & 0fffh, 只保留前12个bits = 1.5Bytes,即截取该FAT表项
    pop bx                          ; 弹出BX
    pop es                          ; 弹出ES
    ret                             ; 返回,回到call Func_GetFATEntry的地方。 返回值AX = 要找的FAT表项

参数AH=FAT表项号,返回值AX是下一个FAT表项号。
通过不断读出FAT表项号,然后根据FAT表项号计算出数据所在的扇区,读出数据到内存,最终将整个文件加载到内存中。

完整内容

;/***************************************************
;       版权声明
;
;   本操作系统名为:MINE
;   该操作系统未经授权不得以盈利或非盈利为目的进行开发,
;   只允许个人学习以及公开交流使用
;
;   代码最终所有权及解释权归田宇所有;
;
;   本模块作者:  田宇
;   EMail:      345538255@qq.com
;
;
;***************************************************/

    org 0x7c00  

BaseOfStack equ 0x7c00

; Loader 程序起始物理地址
BaseOfLoader    equ 0x1000
OffsetOfLoader  equ 0x00

RootDirSectors  equ 14    ; 根目录所占扇区数
SectorNumOfRootDirStart equ 19
SectorNumOfFAT1Start    equ 1
SectorBalance   equ 17  

    jmp short Label_Start        
    nop
    BS_OEMName  db  'MINEboot'      ; 生产厂商名
    BPB_BytesPerSec dw  512         ; 每扇区字节数 512bytes
    BPB_SecPerClus  db  1           ; 每簇扇区数 1
    BPB_RsvdSecCnt  dw  1           ; 保留扇区数 1
    BPB_NumFATs db  2               ; FAT表的份数 2
    BPB_RootEntCnt  dw  224         ; 根目录可容纳扇区数 223
    BPB_TotSec16    dw  2880        ; 总扇区数
    BPB_Media   db  0xf0            ; 介质描述符 0xF0 软盘
    BPB_FATSz16 dw  9               ; 每Fat扇区数 9
    BPB_SecPerTrk   dw  18          ; 每磁道扇区数 18
    BPB_NumHeads    dw  2           ; 磁头数 2
    BPB_HiddSec dd  0               ; 隐藏扇区数 0
    BPB_TotSec32    dd  0           ; 如果BPB_TotSec16为0, 则由这个值记录扇区数
    BS_DrvNum   db  0               ; int 13h 的驱动器号 0
    BS_Reserved1    db  0           ; 未使用 0
    BS_BootSig  db  0x29            ; 扩展引导标记 0x29h
    BS_VolID    dd  0               ; 卷序列号
    BS_VolLab   db  'boot loader' ; 卷标 'boot loader'
    BS_FileSysType  db  'FAT12   '  ; 文件系统类型 'FAT12'

Label_Start:        ; 跳转位置,跳过元数据, boot引导开始

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

;=======    clear screen

    mov ax, 0600h
    mov bx, 0700h
    mov cx, 0
    mov dx, 0184fh
    int 10h

;=======    set focus

    mov ax, 0200h
    mov bx, 0000h
    mov dx, 0000h
    int 10h

;=======    display on screen : Start Booting......

    mov ax, 1301h
    mov bx, 000fh
    mov dx, 0000h
    mov cx, 10
    push    ax
    mov ax, ds
    mov es, ax
    pop ax
    mov bp, StartBootMessage
    int 10h

; 上面是第一部分

;=======    reset floppy  重置软盘

    xor ah, ah
    xor dl, dl
    int 13h

;=======    search loader.bin
; SectorNo 扇区号,
; SectorNumOfRootDirStart 起始扇区号  就是根目录的起点
    mov word    [SectorNo], SectorNumOfRootDirStart             ; 搜索loader.bin

Lable_Search_In_Root_Dir_Begin:

    ; 有一个程序状态寄存器,执行cmp后会给其中几个位(作用不同)设flag
    ; jz 是选择ZF标志位做跳转条件 执行条件是ZF=0
    cmp word    [RootDirSizeForLoop],   0        ; 一个扇区一个扇区地查找loader, 比较Root
    jz  Label_No_LoaderBin                       ; 等于转移 jz的意思是jump if zero 、
    ; 没有跳转的话就往下执行
    dec word    [RootDirSizeForLoop]             ; RootDirSizeForLoop - 1  前往下一个扇区
    mov ax, 00h                                  ; ax=00h
    ; 准备调用读取软盘用的Func_ReadOneSector函数,现在要输入参数
    ; 参数: AX=待读取的磁盘起始扇区号;CL=读入的扇区数量;ES:BX 目标缓冲区起始地址
    mov es, ax                                   ; es=ax=0
    mov bx, 8000h                                ; bx=8000h, 
    mov ax, [SectorNo]                           ; 要读取扇区号
    mov cl, 1                                    ; 读入一个扇区
    call    Func_ReadOneSector                   ; 读入一个扇区,数据装到缓冲区[ES:BX] ([00008000h])
    mov si, LoaderFileName                       ; loader文件名地址装进SI
    mov di, 8000h                                ; DI = 8000H
    cld                                          ; CLD flag方向标志位DF清零,在字串操作中使变址寄存器SI或DI的地址指针自动增加,字串处理由前往后。
                                                 ; SI--, DI--
    mov dx, 10h                                  ; DX = 10h 每个扇区可容纳的目录项的个数 512/32=10h,一个根目录项是32Bytes,前11Bytes是文件名

Label_Search_For_LoaderBin:

    cmp dx, 0                                    ; DX==0?
    jz  Label_Goto_Next_Sector_In_Root_Dir       ; DX==0 => ZF = 1, 说明这个扇区的目录项已经读取完了,要到下一个扇区:跳转至jz   Label_Goto_Next_Sector_In_Root_Dir,
    dec dx                                       ; DX-- 这个扇区还没读完,去找下一个目录项
    mov cx, 11                                   ; cx==11,指目录项的文件名长度

Label_Cmp_FileName:

    cmp cx, 0                                    ; cx==0?
    jz  Label_FileName_Found                     ; 若CX==0,说明每一个字符都匹配,即找到文件,跳转到Label_FileName_Found
    dec cx                                       ; 否则继续比较下一个字符,CX--,
    lodsb                                        ; LODSB/LODSW/LODSD/LODSQ(区别是值的位数)指令:
                                                 ;          从DS:(R|E)SI寄存器指定的内存地址中读取数据到AL/AX/EAX/RAX寄存器
                                                 ;          当数据载入到AL/AX/EAX/RAX寄存器后,(R|E)SI寄存器将会依据R|EFLAGS标志寄存器的DF位(之前用CLD清零的(DF=0))
                                                 ;          自动减少或增加载入的数据长度
                                                 ;          若DF=0, (R|E)SI++,在本程序中即指向文件名下一个字符的地址
    cmp al, byte    [es:di]                      ; AL已经存入了SI指向的字符(AL是我们给出的文件名的一个字符),现在与[es:di]比较,[es:di]是从磁盘中读取到的目录的文件名部分的一个字符的值
    jz  Label_Go_On                              ; 相同,跳转到Label_Go_On,准备比较下一个字符
    jmp Label_Different                          ; 不相同,确认与要搜索的文件名不符,跳转到Label_Different,准备读取下一个目录项

Label_Go_On:

    inc di                                       ; DI++, 移动指向目录项文件名的指针,指向下一个字符
    jmp Label_Cmp_FileName                       ; 比较这个字符

Label_Different:                                
    ; 目录项的文件名不相同
    and di, 0ffe0h                               ; 这个and的目的是,将di指向这个目录项(刚刚搜索的那个)的开头
    add di, 20h                                  ; 20H = 32D,然后跳过这个目录项,到达下一个目录项开头
    mov si, LoaderFileName                       ; 重置我们要搜索的文件名的指针,(因为搜索前面的目录项时,这个指针被动过)
    jmp Label_Search_For_LoaderBin               ; 回到Label_Search_For_LoaderBin

Label_Goto_Next_Sector_In_Root_Dir:

    add word    [SectorNo], 1                    ; SectorNo + 1,扇区号加1,即下一个扇区
    jmp Lable_Search_In_Root_Dir_Begin           ; 去在下一个扇区里搜索

;=======    display on screen : ERROR:No LOADER Found

Label_No_LoaderBin:
; 显示无loader
    mov ax, 1301h
    mov bx, 008ch
    mov dx, 0100h
    mov cx, 21
    push    ax
    mov ax, ds
    mov es, ax
    pop ax
    mov bp, NoLoaderMessage
    int 10h
    jmp $

;=======    found loader.bin name in root director struct

Label_FileName_Found:
; 文件名匹配
    mov ax, RootDirSectors                      ; AX = 根目录所占扇区数
    and di, 0ffe0h                              ; di 重新指向找到的这个目录项的开始
    add di, 01ah                                ; di 跳到起始簇号所在的地方
    mov cx, word    [es:di]                     ; 取出簇号(簇号占2字节=1字)
    push    cx                                  ; cx存栈
    add cx, ax                                  ; CX = CX + AX,得到数据区开头
    add cx, SectorBalance                       ; CX = CX + 根目录起始区号 - 2。 实际上,扇区号 = 数据区簇号(从2开始)*每簇扇区号 + 根目录起始扇区号 + 根目录所占扇区数 - 2
    mov ax, BaseOfLoader                        ; AX = loader程序起始物理地址,最终要把loader加载到内存的这个地方
    mov es, ax                                  ; ES = AX  = loader程序起始物理地址
    mov bx, OffsetOfLoader                      ; BX = OffsetOfLoader
    mov ax, cx                                  ; AX = CX = (数据所在的)扇区号

Label_Go_On_Loading_File:
    push    ax                                  ; AX 压栈
    push    bx                                  ; BX 压栈

    ; 显示 “...”
    mov ah, 0eh                                 ; AH = 0eH
    mov al, '.'
    mov bl, 0fh
    int 10h                                     ; BIOS中断服务程序

    pop bx                                      ; BX 出栈
    pop ax                                      ; AX 出栈
    mov cl, 1                                   ; CL = 1;
    call    Func_ReadOneSector                  ; 读取一个扇区
    pop ax                                      ; 弹出AX
    call    Func_GetFATEntry                    ; 获取FAT表项,FAT表项在AX中返回
    cmp ax, 0fffh                               ; AX == 0fffh? 检查是否是最后一个簇
    jz  Label_File_Loaded                       ; 如果是最后一个簇,跳转到Label_File_Loaded
    push    ax                                  ; AX压栈
    mov dx, RootDirSectors                      ; DX = 根目录所占扇区数
    add ax, dx                                  ; AX = AX + DX 
    add ax, SectorBalance                       ; AX + SectorBalance, AX =  文件所在簇号 + 根目录所占扇区数 + 根目录起始扇区 - 2 = FAT表项所指的簇
    add bx, [BPB_BytesPerSec]                   ; BX + 每扇区字节数,目的是将读出的内容追加到上一次读出的内容后面
    jmp Label_Go_On_Loading_File                ; 跳转至 Label_Go_On_Loading_File,读出下一个扇区

Label_File_Loaded:

    jmp $

;=======    read one sector from floppy
; 软盘读取功能
Func_ReadOneSector:

    push    bp                      ; 栈帧寄存器压栈
    mov bp, sp                      ; 
    sub esp,    2                   ; esp - 2 在在栈中开辟两个字节的新空间
    mov byte    [bp - 2],   cl      ; 参数cl=读入山区的数量 存栈
    push    bx                      ; BX 存栈
    ; 用LBA扇区号计算CHS (Cylinder/Head/Sector,柱面/磁头/扇区)
    mov bl, [BPB_SecPerTrk]         ; 每磁道扇区数装入bl
    ; div 指令根据除数位数选择寄存器,8位为AX, 16位DX:AX,32位EDX:EAX,
    div bl                          ; AX/BL = AL ... AH , LBA扇区号/每磁道扇区数 = 磁道号……磁道内扇区号
    inc ah                          ; 磁道内起始扇区号从1开始,LBA从0开始,所以要+1
    mov cl, ah                      ; AH移动到CL
    mov dh, al                      ; 商AL移动到DH
    shr al, 1                       ; 柱面号 = 磁道号>>1
    mov ch, al                      ; 求得的柱面号移动至CH
    and dh, 1                       ; DH(商)与1与,得到磁头号
    pop bx                          ; 弹出BX
    ; 调用BIOS中断服务程序
    mov dl, [BS_DrvNum]             ; DL参数驱动器号

Label_Go_On_Reading:
    mov ah, 2                       ; 设置中断服务程序,ah=02h,功能是读取磁盘扇区
    mov al, byte    [bp - 2]        ; 读入的扇区数,放入al, [bp-2]是之前输入的参数,指读入扇区数
    int 13h                         ; BIOS中断服务程序
    ; 中断服务程序从软盘扇区读取数据到内存中,数据读取成功时CF复位,CF=0
    ; 此时轮询CF位,直到读取完成,完成时CF=0
    jc  Label_Go_On_Reading         ; CF = 1 回到这一块代码开始

    add esp,    2                   ; 收回2个字节的栈空间
    pop bp                          ; bp出栈
    ret                             ; 返回
    ; RET
    ; 弹出栈中的保存的返回地址(之前被CALL存进去的),回到了116 call Func_ReadOneSector
;=======    get FAT Entry

Func_GetFATEntry:
; 获取FAT表项
    push    es                      ; ES 存栈
    push    bx                      ; BX 存栈
    push    ax                      ; AX 存栈
    mov ax, 00                      ; AX = 0
    mov es, ax                      ; ES = AX = 0
    pop ax                          ; AX 出栈
    mov byte    [Odd],  0           ; 标志位Odd = 0
    mov bx, 3                       ; BX = 3
    mul bx                          ; AX = BX * AX
    mov bx, 2                       ; BX = 2
    div bx                          ; AX / BX = AX....DX  

    cmp dx, 0                       ; 比较DX,若为奇数,DX=1,若为偶数则DX=0
    jz  Label_Even                  ; 若为偶数,跳转到Label_Even,此时Odd=0
    mov byte    [Odd],  1           ; 否则标志位Odd设为1

Label_Even:

    xor dx, dx                      ; DX = DX ^ DX = 0, 这样写比mov使用更少的字节
    mov bx, [BPB_BytesPerSec]       ; BX = 每扇区字节数
    div bx                          ; AX = Fat表项偏移扇区号,DX = FAT表项在扇区中的偏移位置
    push    dx                      ; DX 存栈
    mov bx, 8000h                   ; BX = 8000H,目标缓冲区起始地址
    add ax, SectorNumOfFAT1Start    ; AX = AX + FAT1表起始扇区号(1),得到需要的FAT项所在的的扇区号,作为ReadOneSector的参数之一
    mov cl, 2                       ; CL = 2 指示ReadOneSector读入两个扇区
    call    Func_ReadOneSector      ; 调用ReadOneSector,把FAT表项所在的扇区读入到8000H

    pop dx                          ; DX 出栈
    add bx, dx                      ; BX = BX + DX,BX = 目标缓冲区起始地址 + FAT表项在扇区中的偏移位置
    mov ax, [es:bx]                 ; 读出FAT表项到AX
    cmp byte    [Odd],  1           ; Odd == 1?
    jnz Label_Even_2                ; Odd != 1 , 跳转到Label_Even_2
    shr ax, 4                       ; 奇数项右移4位(半字节),把属于前一个FAT表项的部分去掉

Label_Even_2:
    and ax, 0fffh                   ; AX & 0fffh, 只保留前12个bits = 1.5Bytes,即截取该FAT表项
    pop bx                          ; 弹出BX
    pop es                          ; 弹出ES
    ret                             ; 返回,回到call Func_GetFATEntry的地方。 返回值AX = 要找的FAT表项

;=======    tmp variable

RootDirSizeForLoop  dw  RootDirSectors
SectorNo        dw  0
Odd         db  0

;=======    display messages

StartBootMessage:   db  "Start Boot"
NoLoaderMessage:    db  "ERROR:No LOADER Found"
LoaderFileName:     db  "LOADER  BIN",0

;=======    fill zero until whole sector

    times   510 - ($ - $$)  db  0
    dw  0xaa55

运行效果

(0)

本文由 橙叶博客 作者:FrankGreg 发表,转载请注明来源!

热评文章

评论:

4 条评论,访客:4 条,博主:0 条
  1. jianala
    jianala发布于: 

    部署完了,一直在等域名和备案还有SSL证书~~审核,我博客~,没美化的~119.3.232.255 除了侧面栏都是动态加载的~

    • FrankGreg
      FrankGreg发布于: 

      收到~域名好了之后跟我说,我加上你的友链

  2. 未遂
    未遂发布于: 

    兄弟,想问个问题,如何实现NEXTCLOUD的存储目录从根目录调整到HOME去。能解答,不胜感激!谢谢 。我尝试QQ登陆,登陆不上。我QQ77255631

    • FrankGreg
      FrankGreg发布于: 

      把data复制到home下,设置好权限,在config.php里修改路径即可

发表评论