zoukankan      html  css  js  c++  java
  • FAT12格式的引导区实现

    org 07c00h
    
    
    ;================================================
    jmp short START        
    nop                ; 这个 nop 不可少
    
    ;这个结构将要被写在软盘的第一个扇区,相当于格式化软盘为FAT12格式
    BS_OEMName    DB 'PAVKOOOO'    ; OEM String, 必须 8 个字节
    BPB_BytsPerSec    DW 512        ; 每扇区512字节
    BPB_SecPerClus    DB 1        ; 每簇1扇区   簇的定义是为了操作系统能够更加快速的去硬盘寻址,快速定位,而不只是使用扇区号
    BPB_RsvdSecCnt    DW 1        ; 引导区使用1个扇区,不能有其他值,因为BIOS启动之后就会去取软盘的第一个扇区所有数据到内存,然后将控制权交给这个引导区
    BPB_NumFATs    DB 2        ; 共有2 FAT 表 ;FAT格式(12,16,32)几乎都是2个表,第二个表用来备份。所以内容和第一个表完全一样
    BPB_RootEntCnt    DW 224        ; 最多能存储224个文件??不清楚为什么是这个值
    BPB_TotSec16    DW 2880        ; 逻辑扇区总数:FAT12的扇区个数=2个磁头*18个磁道*80个扇区 =2880
    BPB_Media    DB 0xF0        ; 媒体描述符 ??
    BPB_FATSz16    DW 9        ; 每FAT扇9个
    BPB_SecPerTrk    DW 18        ; 每磁道18个扇区
    BPB_NumHeads    DW 2        ; 磁头数(面数)
    BPB_HiddSec    DD 0        ; 隐藏扇区数
    BPB_TotSec32    DD 0        ; 总扇区数,如果前面16位已经记录,这个就为0
    BS_DrvNum    DB 0        ; 中断 13 的驱动器号
    BS_Reserved1    DB 0        ; 未使用
    BS_BootSig    DB 29h        ; 扩展引导标记 (29h)
    BS_VolID    DD 0        ; 卷序列号
    BS_VolLab    DB 'PAVKOOOOOOO'; 卷标, 必须 11 个字节
    BS_FileSysType    DB 'FAT12   '    ; 文件系统类型, 必须 8个字节  
    
    BaseofStack equ 07c00h
    FileEntryBegin equ 19
    CsOfLoader equ 010000h    ;在实模式下,将loader.bin加载的内存地址应该为 07c00h+引导程序(512=200h)=07e00h之后
    ipofLoader equ 0100h      ;确保程序加载的内容在实模式寻址空间之内:1MB 
    FileEntryCount equ 14     ;文件目录区所占用的扇区数
    ;=============引导程序开始执行====================
    START:
    xor eax,eax
    mov ax,cs
    mov ds,ax      ;数据段,视频段,附加段都指向当前段
    mov ss,ax
    mov sp,BaseofStack    ;栈顶ss:sp ==> 07C00h 也就是程序开始的位置 ,栈往低地址生长,这里没有做堆栈溢出检查
    
    ;复位软驱
    xor ah,ah
    xor dl,dl
    int 13h
    ;在软盘中读取1个扇区到内存
    ;因为loader.bin存在于根目录区,根目录区在扇区号19(0---引导扇区,1~9---FAT表1,10~18---FAT表2),所以需要将19扇区加载到内存
    ;整个目录区占用了多少个扇区? 224(个文件)*32(每个文件占用32个字节)/512(每个扇区512个字节) = 14 占用了14个扇区。
    mov LoopSecNo,FileEntryBegin
    BEGINREADSEC:
    cmp LoopOfSec,0
    jz NOLOADER
    dec LoopOfSec
    mov ax,CsOfLoader
    mov es,ax
    mov ax,ipofLoader
    mov bx,ax     ;将数据读到es:bx指向的缓存中
    mov ax,LoopSecNo
    mov cl,1    ;只读1个扇区
    call    ReadSector
    
    ;读完之后,内存里就有512字节的内容了,现在还是寻找loader.bin
    mov    si, LoaderFileName    ; ds:si -> "LOADER  BIN"
    mov    di, ipofLoader
    cld
    mov    dx, 10h
        ;一个扇区512 / 32有16(10H)个目录项
    BEGINLOOPFILENAME:    
        ;开始一个个项目的比较
        cmp    dx, 0
        jz NEXTSECTOR   ;到下一个扇区寻找,所以需要重新加载一个扇区    
        dec    dx
        
        ;11个字母的文件名                                            ; ┛就跳到下一个 Sector
        mov    cx, 11
        COMPARENAME:
            cmp    cx, 0
            jz FILEFOUND ;文件找到了
                dec    cx
                lodsb                ; ds:si -> al
                cmp    al, byte [es:di]
                jz NEXTCHAR 
                jmp NEXTFILE
            NEXTCHAR:
            inc di        
            jmp COMPARENAME
        NEXTFILE:
        and    di, 0FFE0h   loadsb会改变di,所以需要将di指定到文件条目的首地址
        add    di, 20h   ;每个文件条目占有32个字符=20H
        jmp BEGINLOOPFILENAME    
    
    NEXTSECTOR:
        add LoopSecNo,1
        jmp BEGINREADSEC
    
    NOLOADER:
        jmp $    ;找不到的话,就卡死到这里
    ;如果文件找到了,就要把文件从数据区读取到内存
    ;目录区之后就是数据区,所以数据区的第一个扇区号是19+14=33!
    FILEFOUND:
    and    di, 0FFE0h        ; di -> 当前条目的开始
    add    di, 01Ah ;01Ah = 26 也就是文件条目地址偏移26的位置:DRI_FSTCLUS
    
    ;文件所在的数据区扇区号为:dir.DIR_FileSize -2 + 33   ==> [es:di]
    mov word ax,[es:di];
    push ax   ;保存序号,getfatentry的时候需要使用
    sub ax 2
    add ax 33
    LABEL_GOON_LOADING_FILE:
        mov cl,1
        ReadSec
    
        ;loader.bin可能大于512字节,就是说可能超过1个扇区(不能超过两个,因为现在在实模式,实模式的最大寻址为1M)
        ;所以需要读取fat表,查看是否还有其他的扇区
    
        pop ax
        call GetFATEntry
        cmp    ax, 0FFFh
            jz    FILELOADED
            ;读取下一个未完的扇区
            sub ax 2
            add ax 33        
            ;将数据读到es:bx指向的缓存中
            add    bx, [BPB_BytsPerSec]     ;    读取的内存指针也应该后移512个字节(1个扇区)
        jmp    LABEL_GOON_LOADING_FILE
    
    
    FILELOADED:
    ; **********************
    ; 这一句正式跳转到已加载到内
    ; 存中的 LOADER.BIN 的开始处,
    ; 开始执行 LOADER.BIN 的代码。
    ; Boot Sector 的使命到此结束。
    jmp    CsofLoader:ipofLoader    
    ; **********************
    
    ;----------------------------------------------------------------------------
    ; 函数名: GetFATEntry
    ;----------------------------------------------------------------------------
    ; 作用:
    ;    找到序号DRI_FSTCLUS为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中
    ;    需要注意的是, 中间需要读 FAT 的扇区到 es:bx 处, 所以函数一开始保存了 es 和 bx
    
    GetFATEntry:
        push    es
        push    bx
        push    ax
        mov    ax, CsOfLoader; `.
        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:;偶数
        ; 现在 ax 中是 FATEntry 在 FAT 中的偏移量,下面来
        ; 计算 FATEntry 在哪个扇区中(FAT占用不止一个扇区)
        xor    dx, dx            
        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
        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
    
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;如何函数使用INT 13读取软盘
    ;http://en.wikipedia.org/wiki/INT_13H
    ;传入扇区号到ax
    ;读取扇区数目到cl
    ;将数据读到es:bx指向的缓存中
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;在函数里面最好使用bp而不是使用SP
    ReadSec:
    push bp
        mov bp,sp
        sub sp,2
            mov bype [bp-2],cl
            push bx
                mov bl,[BPB_SecPerTrk]    
                div bl   ;扇区号/18; ah=余数,al=商
                inc ah   ;扇区号从0开始,所以需要加1
                mov cl,ah
                mov dh,al   ;al表示磁道号
                shr al,1    ;al / 2 的值为磁道号
                mov ch,al
                and dh,1    ;dh磁头号
                mov dl=[BS_DrvNum]
            pop bx
            ;int 13所需要的值都已经附了
            LoopWhenFeild:
                mov ah,2
                mov cl,[bp-2]
                int 13
            jc LoopWhenFeild
        add sp,2
    pop bp
    ret
    
    
    
    ;-.- -.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.-!
    ;变量定义区
    LoaderFileName        db    "LOADER  BIN", 0    ; LOADER.BIN 之文件名
    LoopOfSec dw FileEntryCount;
    LoopSecNo dw 0;
    bOdd            db    0        ; 奇数还是偶数
    ;----------------------------------------------------------------------------
    
    times     510-($-$$)    db    0    ; 填充剩下的空间,使生成的二进制代码恰好为512字节
    dw     0xaa55                ; 结束标志
  • 相关阅读:
    subprocess 子进程模块
    3.5 魔法方法
    ThinkPHP中,display和assign用法详解
    linux常用指令
    退出当前Mysql使用的db_name 的方式
    PHP中GD库是做什么用的? PHP GD库介绍11111111
    include跟include_once 以及跟require的区别
    全局变量跟局部变量
    关于define
    创建、删除索引---高级部分
  • 原文地址:https://www.cnblogs.com/pavkoo/p/3643538.html
Copyright © 2011-2022 走看看