码迷,mamicode.com
首页 > 其他好文 > 详细

第十二天、可以读 FAT12 了

时间:2015-11-11 18:02:01      阅读:266      评论:0      收藏:0      [点我收藏+]

标签:

    搞了三天,总算把那个虫子找到了,可以写出来了。

    引导扇区只有 512 字节,太小,基本啥也干不了,我们只能利用引倒扇区把我们的操作系统内核载入内存。但是一下子把内核全部载入进来也不靠谱,还有很多事情没做呢——起码就还没转到安全模式,内核大的话,实模式可装不下。所以正确的姿势是:引导扇区载入一个装载程序,装载程序负责做好准备工作后,再载入真正的内核。

    那么引导扇区的任务就很明确了:找到装载程序(Loader.bin),然后把它载入内存。第十天初识FAT12时提到了:通过根目录表项找到文件的第 1 个扇区,再从 FAT 表中找到其他的扇区。代码有很详细的注释:

; BootSector.nas
; 引导扇区
; 四彩
; 2015-11-11

; ========================================================================================
; 电脑的启动过程:
; 1、PC 通电启动后,CPU 马上跳到系统 BIOS 中的启动代码处。
; 2、系统 BIOS 的启动代码首先进行硬件检查,并加载相关硬件设备。
; 3、检查完成后,把用户指定磁盘的第一个扇区读到内存 0x7C000 处,然后转交控制权。
; 4、操作系统就从内存 0x7C00 处开始接管电脑。
; 5、内存0x7E00 ~ 0x9FBFF 未定义,由操作系统使用。
; ****************************************************************************************


[SECTION .text]
; ========================================================================================
; 头文件及常量定义
; ----------------------------------------------------------------------------------------
%include "./Boot/INC/FAT12.inc"
; ----------------------------------------------------------------------------------------
AbsMemAddrOfBoot    equ 0x7C00              ; BootSector 被加载到内存的绝对地址
SegAddrOfLoader     equ 0x7E00              ; LOADER.BIN、临时数据被加载到内存的段地址
OffsetOfLoadLoader  equ 1024                ; LOADER.BIN 被加载到内存的偏移量
OffsetOfLoadTemp    equ 0                   ; 临时数据被加载到内存的偏移量(最多 2 个扇区)
; ----------------------------------------------------------------------------------------
    org AbsMemAddrOfBoot
; ****************************************************************************************


; ========================================================================================
; FAT12 文件系统引导扇区的头部(前 62 字节)
    FAT12Head _main, "NASM+GCC", "TestX_v0.01"
; ****************************************************************************************


; ========================================================================================
; FAT12 文件系统引导扇区的引导代码(从第 62 字节开始)
; ----------------------------------------------------------------------------------------
; 程序入口
_main:
    ; 初始化寄存器
    mov ax, cs
    mov bp, ax
    mov ds, ax
    mov ax, SegAddrOfLoader
    mov es, ax
    cld

    ; 寻找 Loader.bin
    call SearchLoaderFirstSector

    ; 加载 Loader.bin
    mov bx, OffsetOfLoadLoader
    call LoadLoader

    ; 控制权交给已加载到内存的 loader
    jmp SegAddrOfLoader : OffsetOfLoadLoader

; 以下定义子函数
; ----------------------------------------------------------------------------------------
; 函数功能:寻找 loader 文件的起始扇区
; 入口参数:ds : si = loader 文件名的存放地址
; 出口参数:eax = loader 文件的起始逻辑扇区号
; 寄存器:ax、bx、cx、dx、si
SearchLoaderFirstSector:
    push bp
    mov bp, sp
    sub sp, 2 * 4                           ; 32 位通用寄存器长 4 字节

    ; 待读取的根目录区逻辑扇区号
    mov ax, IRootDirectoryFirstSector
    mov [bp + 2 * 4], ax
    ; 待查找的根目录区扇区数
    mov ax, IDataFirstSector - IRootDirectoryFirstSector
    mov [bp + 3 * 4], ax

    ; 逐个扇区寻找
.Search_NextSector:
    mov ax, [bp + 2 * 4]
    mov bx, OffsetOfLoadTemp
    call Read1Sector
                                            ; cx 统计一个扇区内未匹配的表项数
    mov cx, 16                              ; = [BPB_BytsPerSec] / DirectoryItem_size
.Search_ThisSector:
    ; 匹配文件名
    mov si, LoaderFileName
    mov dx, 11                              ; dx 统计未匹配的文件名字符数
.Match_FileName:
    mov al, byte[ds : si]
    mov ah, byte[es : bx]
    cmp al, ah
    jnz .Match_NextItem
    dec dx
    cmp dx, 0
    jz .Found
    inc bx
    inc si
    jmp .Match_FileName

.Match_NextItem:
    and bx, 0b1111111111100000              ; 回当前表项的开始处
    add bx, 32                              ; 指向下一个表项(一个表项 32 字节,占用 5 位)
    loop .Search_ThisSector

    ; 判断是否读完根目录区所有扇区:读完说明没找到,没读完就继续下一个
    dec word[bp + 3 * 4]
    cmp word[bp + 3 * 4], 0
    jz .NotFound
    inc word[bp + 2 * 4]
    jmp .Search_NextSector

.NotFound:
    call PrintMSG
    db "Failed to search Loader.bin !", `\r\n`, 0
    jmp $

.Found:
    mov ax, word[es : bx + 16]              ; 指向当前表项中的 .DIR_FstClus(26 - 11 + 1)
    add ax, IDataFirstSector - 2

    add sp, 2 * 4
    pop bp
    ret

; ----------------------------------------------------------------------------------------
; 函数功能:从软盘装载文件到内存
; 入口参数:ax = 该文件第一个逻辑扇区号
;           es : bx = 存放数据的内存缓冲区地址
; 出口参数:无
; 寄存器:ax、bx,cx、dx
LoadLoader:
    push bx
    push ax
    call Read1Sector
    pop ax

    call GetNextSector
    pop bx

    cmp ax, 0xFF8                           ; FAT 表项的值大于等于 0xFF8,表示文件结束
    jae .Return

    add ax, IDataFirstSector - 2            ; FAT 表项的序号 2 对应数据区第 1 个扇区
    add bx, [BPB_BytsPerSec]
    jmp LoadLoader

.Return:
    ret

; ----------------------------------------------------------------------------------------
; 函数功能:取得逻辑扇区在 FAT 表中的表项值
; 入口参数:ax = 逻辑扇区号
; 出口参数:ax = 对应的 FAT 表项值(即下一个扇区的序号)
; 寄存器:ax、bx、dx,cx
GetNextSector:
    push bp
    mov bp, sp
    sub sp, 4

    ; 保存 FAT 表项号的奇偶性(0 = 偶数,1 = 奇数)
    mov dword[bp + 2 * 4], 0

    ; 计算逻辑扇区号 ax 在 FAT 表中的表项号(ax * 12 / 8)
    xor dx, dx
    sub ax, IDataFirstSector - 2
    mov bx, 12
    mul bx
    mov bx, 8
    div bx

    ; 校验 FAT 表项号的奇偶性
    cmp dx, 0
    jz .Label_GetNextSector_1
    mov dword[bp + 2 * 4], 1

.Label_GetNextSector_1:
    ; 计算该表项号所在的逻辑扇区号和在该扇区的偏移量
    xor dx, dx
    mov bx, [BPB_BytsPerSec]
    div bx
    push dx
    add ax, [BPB_RsvdSecCnt]

    ; 读取连续 2 个扇区(表项可能跨扇区)
    push ax
    mov bx, OffsetOfLoadTemp
    call Read1Sector
    pop ax
    inc ax
    add bx, [BPB_BytsPerSec]
    call Read1Sector

    ; 根据偏移量读出 16 位,奇数项去掉低 4 位,偶数项去掉高 4 位,得到相应的 12 位项值
    ; ——  FAT 表前面 3 个字节记录信息占用了 2 个 序号(第一个有效序号 2 从字节 3 开始)
    pop bx                                  ; 上面压进去的 dx 值(偏移量)
    mov ax, [es : bx]

    cmp dword[bp + 2 * 4], 0
    jz .Label_GetNextSector_2
    shr ax, 4
.Label_GetNextSector_2:
    and ax, 0b0000111111111111              ; 奇数项高 4 位已为 0 执行此操作值也不变

    add sp, 4
    pop bp
    ret

; ----------------------------------------------------------------------------------------
; 函数功能:从软盘读取 1 个逻辑扇区
; 入口参数:ax = 起始逻辑扇区号
;           es : bx = 存放数据的内存缓冲区地址(同 int 0x13、ah = 2)
; 出口参数:同 int 0x13、ah = 2
; 寄存器:ax、cx、dx
Read1Sector:
    ; 由 LBA 计算 CHS
    mov dl, 18
    div dl
    mov ch, al
    mov dh, al
    mov cl, ah
    shr ch, 1
    inc cl
    and dh, 1

    ; 读一个扇区
    mov ax, 0x0201
    xor dl, dl
    int 0x13

;   cmp ah, 0                               ; 虚拟软盘不会出错
;   jz .Return
;   call PrintMSG
;   db "Error to read Floppy Disk !", `\r\n`, 0
;   jmp $
;.Return:
    ret

; ----------------------------------------------------------------------------------------
; 函数功能:显示紧跟在调用指令后定义的字符串
; 入口参数:无
; 出口参数:无
; 寄存器:ax、bx、si
PrintMSG:
    mov ah, 0x0e                            ; 功能号:0x0E 显示字符,光标跟随字符移动
    xor bx, bx                              ; bh = 页码,bl = 前景色(图形模式)

.Loop:
    ; 逐个取字符
    pop si
    lodsb
    push si

    cmp al, 0                               ; 字符串以 0 结尾
    je .Return
    int 0x10
    jmp .Loop

.Return:
    ret
; ****************************************************************************************


; ========================================================================================
; FAT12 文件系统引导扇区引导代码的剩余部分用 0 填满
    times 497 - ($ - $$) db 0
    LoaderFileName  db "LOADER  BIN", 0, 0  ; loader 文件名(8 + 3格式,长度不够的填空格)
; ****************************************************************************************


; ========================================================================================
; FAT12 文件系统引导扇区的的结束标志(最后 2 字节,必须是 0xAA55)
    dw 0xAA55
; ****************************************************************************************
; FAT12.inc
; FAT12 文件系统常量及宏定义
; 四彩
; 2015-11-08

; ========================================================================================
IRootDirectoryFirstSector   equ 19  ; 根目录区的起始逻辑扇区号
IDataFirstSector            equ 33  ; 数据区的起始逻辑扇区号
; ****************************************************************************************

; ========================================================================================
; FAT12 文件系统的引导扇区头部(前 62 字节)格式宏
; 调用格式:FAT12Head Label_RealEntry, OEMName, VolLab
;           Label_RealEntry : 程序入口标签
;           OEMName         : 厂商名称(8 字节长,不够的填空格)
;           VolLab          : 卷标(11 字节长,不够的填空格)
%macro FAT12Head 3
;   名称                        偏移 长度     内容                    3.5英寸软盘值
    jmp %1                      ; 0   3   跳转指令,指向程序入口      jmp Label_RealEntry
    nop
    BS_OEMName      db %2       ; 3   8   厂商名称                    自行定义
    BPB_BytsPerSec  dw 512      ; 11  2   每扇区字节数                512
    BPB_SecPerClus  db 1        ; 13  1   每簇扇区数                  1
    BPB_RsvdSecCnt  dw 1        ; 14  2   保留扇区数                  1
    BPB_NumFATs     db 2        ; 16  1   FAT表份数                   2
    BPB_RootEntCnt  dw 224      ; 17  2   根目录中最多容纳的文件数    224
    BPB_TotSec16    dw 2880     ; 19  2   扇区总数 (FAT12、16)      2880
    BPB_Media       db 0xF0     ; 21  1   介质描述符                  0xF0
    BPB_FATSz16     dw 9        ; 22  2   每个FAT表所占的扇区数       9
    BPB_SecPerTrk   dw 18       ; 24  2   每磁道扇区数                18
    BPB_NumHeads    dw 2        ; 26  2   磁头数                      2
    BPB_HiddSec     dd 0        ; 28  4   隐藏扇区数                  0
    BPB_TotSec32    dd 2880     ; 32  4   扇区总数(FAT32)           2880
    BS_DrvNum       db 0        ; 36  1   磁盘驱动器号                0
    BS_Reserved1    db 0        ; 37  1   保留(供NT使用)            0
    BS_BootSig      db 0x29     ; 38  1   扩展引导标记                0x29
    BS_VolD         dd 0        ; 39  4   卷标序列号                  0
    BS_VolLab       db %3       ; 43  11  卷标                        自行定义
    BS_FileSysType  db ‘FAT12‘  ; 54  8   文件系统类型名              FAT12
;                                       62  448 引导代码、数据及其他填充字符
;                                       510 2   结束标志              0xAA55
;
; BPB:BIOS Parameter Block,BIOS 参数块;BS:Boot Sector,引导扇区
%endmacro
; ****************************************************************************************

; ========================================================================================
; 目录表项结构
struc DirectoryItem
    .DIR_Name       resb    11      ; 文件名 8 字节,扩展名 3 字节
    .DIR_Attr       resb    1       ; 文件属性
                    resb    10      ; 保留
    .DIR_WrtTime    resw    2       ; 最后修改时间
    .DIR_WrtDate    resw    2       ; 最后修改日期
    .DIR_FstClus    resw    2       ; 此条目对应的开始簇号
    .DIR_FileSize   resd    4       ; 文件大小
endstruc
; ****************************************************************************************


第十二天、可以读 FAT12 了

标签:

原文地址:http://my.oschina.net/u/580100/blog/528955

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!