zoukankan      html  css  js  c++  java
  • <自己动手写操作系统>2011032101

    阅读<使用开源软件-自己动手写操作系统>一书中关于“让启动扇区加载引导文件”,从理论上实践了如何做,由于此书中的代码是:AT&T编写的,一时转不过语法;需要手工转换成INTER下的nasm。

    自己转换一小部分代码,感觉有些晦涩,看来自己还在汇编上功夫不够深,需继续努力!

    好了,在网络上找到一份nasm代码,和书中代码功能一致的,地址:http://blog.csdn.net/DL88250/archive/2007/03/22/1537377.aspx

    在此转来这份代码:

    ; loader.asm

    org    0100h

    mov    ax, 0B800h;0B800h是PC机显存的开始地址,http://hi.baidu.com/numax/blog/item/9c51e464f8d736f8f6365439.html

    mov    gs, ax

    mov    ah, 0Fh                ; 0000: 黑底    1111: 白字

    mov    al, 'L'

    mov    [gs:((80 * 0 + 39) * 2)], ax    ; 屏幕第 0 行, 第 39 列。

    jmp    $        ; Start

    /*********************************************************************/
    org  07c00h            ; Boot状态, Bios将把 Boot Sector加载到 0:7C00处并开始执行

    ;=====================================================================================

    BaseOfStack        equ    07c00h    ; Boot状态下堆栈基地址(栈底, 从这个位置向低地址生长)

    BaseOfLoader        equ    09000h    ; LOADER.BIN 被加载到的位置 ----  段地址

    OffsetOfLoader        equ    0100h    ; LOADER.BIN 被加载到的位置 ---- 偏移地址

    RootDirSectors        equ    14    ; 根目录占用空间

    SectorNoOfRootDirectory    equ    19    ; Root Directory 的第一个扇区号

    SectorNoOfFAT1        equ    1    ; FAT1 的第一个扇区号    = BPB_RsvdSecCnt

    DeltaSectorNo        equ    17    ; DeltaSectorNo = BPB_RsvdSecCnt + (BPB_NumFATs * FATSz) - 2

                        ; 文件的开始Sector号 = DirEntry中的开始Sector号 + 根目录占用Sector数目 + DeltaSectorNo

    ;=====================================================================================

        jmp short LABEL_START        ; Start to boot.

        nop                          ; 这个 nop不可少

        ; 下面是 FAT12 磁盘的头

        BS_OEMName    DB 'ForrestY'    ; OEM String, 必须 8 个字节

        BPB_BytsPerSec    DW 512        ; 每扇区字节数

        BPB_SecPerClus    DB 1        ; 每簇多少扇区

        BPB_RsvdSecCnt    DW 1        ; Boot 记录占用多少扇区

        BPB_NumFATs    DB 2        ; 共有多少 FAT 表

        BPB_RootEntCnt    DW 224        ; 根目录文件数最大值

        BPB_TotSec16    DW 2880        ; 逻辑扇区总数

        BPB_Media    DB 0xF0        ; 媒体描述符

        BPB_FATSz16    DW 9        ; 每FAT扇区数

        BPB_SecPerTrk    DW 18        ; 每磁道扇区数

        BPB_NumHeads    DW 2        ; 磁头数(面数)

        BPB_HiddSec    DD 0        ; 隐藏扇区数

        BPB_TotSec32    DD 0        ; 如果 wTotalSectorCount 是 0 由这个值记录扇区数

        BS_DrvNum    DB 0        ; 中断 13 的驱动器号

        BS_Reserved1    DB 0        ; 未使用

        BS_BootSig    DB 29h        ; 扩展引导标记 (29h)

        BS_VolID    DD 0        ; 卷序列号

        BS_VolLab    DB 'Tinix0.01  '; 卷标, 必须 11 个字节

        BS_FileSysType    DB 'FAT12   '    ; 文件系统类型, 必须 8个字节 



    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



        mov    dh, 0                ; "Booting  "

        call    DispStr            ; 显示字符串

       

        xor    ah, ah    ; ┓

        xor    dl, dl    ; ┣ 软驱复位

        int    13h        ; ┛

       

    ; 下面在 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



        mov    si, LoaderFileName    ; ds:si -> "LOADER  BIN"

        mov    di, OffsetOfLoader    ; es:di -> BaseOfLoader:0100 = BaseOfLoader*10h+100

        cld

        mov    dx, 10h

    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                        ; else ┓    di &= E0 为了让它指向本条目开头

        add    di, 20h                            ;     ┃

        mov    si, LoaderFileName                    ;     ┣ di += 20h  下一个目录条目

        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            ; 显示字符串
        jmp    $                    ; 没有找到 LOADER.BIN, 死循环在这里



    LABEL_FILENAME_FOUND:            ; 找到 LOADER.BIN 后便来到这里继续

        mov    ax, RootDirSectors

        and    di, 0FFE0h                ; di -> 当前条目的开始

        add    di, 01Ah                    ; di -> 首 Sector

        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                ; ┃

        pop    bx                    ; ┃

        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]

        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

        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                ; 结束标志

    /*************************************************************/

    编译后,挂接到xp虚拟机上,把loader.bin拷贝到软驱里面。另外启动空系统,挂接boot,这时候会出现“Ready...”之类的字符,说明加载loader.bin成功

    在此,仅仅是以试验方式观察了loader.bin,还需要从代码上继续学习个部分,后续blog学习代码的应用,至少要达到正确且犹如“盲打”一般了解各语句的实际意义(Why,What,How)~
  • 相关阅读:
    Delphi6函数大全(1)
    chr码值对应列表大全
    delphi控制POS打印机
    java network programming 第七章
    loop msn 2
    java ftp 资源
    java network programming 第六章
    IM模型的几个概念
    TCP/IP网络互连技术 卷3 winsock篇
    看你网络安全的水平!!
  • 原文地址:https://www.cnblogs.com/GoGoagg/p/1990254.html
Copyright © 2011-2022 走看看