首页
社区
课程
招聘
[分享][分享]软盘引导程序学习-Masm 实现
发表于: 2013-7-16 12:59 7038

[分享][分享]软盘引导程序学习-Masm 实现

2013-7-16 12:59
7038

于渊自己动手写操作系统
自己用 masm 对照书中 ch4-b 的程式
有闲的朋友进来参考与指正........

        .model tiny
        .code
    org    100h

BaseOfStack             equ 0100h   ; 调试状态下堆栈基地址(栈底, 从这个位置向低地址生长)
BaseOfLoader            equ 09000h  ; LOADER.BIN 被加载到的位置 ----  段地址
OffsetOfLoader          equ 0100h   ; LOADER.BIN 被加载到的位置 ---- 偏移地址
RootDirSectors          equ 14      ; 根目录占用空间
SectorNoOfRootDirectory equ 19      ; Root Directory 的第一个扇区号

start:
    jmp short begin                 ; 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 0F0h         ; 媒体描述符
    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个字节

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

    xor     ah, ah      ; ┐
    xor     dl, dl      ; ├ 软驱复位
    int     13h         ; ┘

    jmp     @F
        wSectorNo           dw      ?
        wRootDirSizeForLoop dw      ?       ; Root Directory 占用的扇区数,
    @@:                                     ; 在循环中会递减至零.
    mov     ax,SectorNoOfRootDirectory      ; 根目录的第一个磁区号 19
    mov     wSectorNo, ax
    mov     ax,RootDirSectors               ; 根目录占用空间 14
    mov     wRootDirSizeForLoop,ax
;111-搜寻根目录表项 14 个-1111111111111111111111111111111111111111111111111111111111111
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
    mov     ax,wRootDirSizeForLoop
    cmp     ax, 0                       ; ┐
    jz      LABEL_NO_LOADERBIN          ; ├ 判断根目录区是不是已经读完
    dec     ax                          ; ┘ 如果读完表示没有找到 LOADER.BIN
    mov     wRootDirSizeForLoop,ax
;111-搜寻根目录 14 个磁区-1111111111111111111111111111111111111111111111111111111111111

        ;2222-将 cl(1) 个根目录磁区读入 es:bx 中-22222222222222222222222222222222222222
        mov ax, BaseOfLoader
        mov es, ax                          ; es <- BaseOfLoader
        mov bx, OffsetOfLoader              ; es:bx = BaseOfLoader:OffsetOfLoader
        mov ax, wSectorNo                   ; ax <- Root Directory 中的某 Sector 号
        mov cl, 1
        call    ReadSector
        ;2222-将 cl(1) 个根目录磁区读入 es:bx 中-22222222222222222222222222222222222222

            ;3333-搜寻根目录表项-1磁区16个档案记录(FDB)-0333333333333333333333333333333
            jmp @F
                LoaderFileName      db  "LOADER  BIN", 0
            @@:
            cld                             ;当DF=0时,变址寄存器SI(和DI)增加1、2或4
            mov si, offset LoaderFileName   ; ds:si -> "LOADER  BIN"
            mov di, OffsetOfLoader          ; es:di -> BaseOfLoader:0100
            mov dx, 10h             ; 1磁区=512 Bytes , 512/32=16=10h 个档案记录(FDB)
        LABEL_SEARCH_FOR_LOADERBIN:
            cmp dx, 0                               ; ┐循环次数控制,
            jz  LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR  ; ├如果已经读完了一个 Sector,
            dec dx                                  ; ┘就跳到下一个 Sector
            ;3333-搜寻根目录表项-1磁区16个档案记录(FDB)-0333333333333333333333333333333

                ;4444-比对"LOADER  BIN"-44444444444444444444444444444444444444444444444
                mov cx, 11
            LABEL_CMP_FILENAME:
                cmp cx, 0
                jz  LABEL_FILENAME_FOUND    ; 如果比较了 11 个字符都相等, 表示找到
                dec cx
                lodsb                       ; ds:si -> al
                cmp al, byte ptr es:[di]
                jz  LABEL_GO_ON
                jmp LABEL_DIFFERENT         ; 只要发现不一样的字符就表明此根目录表项
                                            ; 不是我们要找的 LOADER.BIN
            LABEL_GO_ON:
                inc di
                jmp LABEL_CMP_FILENAME      ; 继续循环
                ;4444-比对"LOADER  BIN"-44444444444444444444444444444444444444444444444

            ;3333-搜寻根目录表项-1磁区16个档案记录(FDB)-0333333333333333333333333333333
        LABEL_DIFFERENT:
                        ; 32 = 20h = 2 ^5        1 2345 (此 5 位元清 0 )
                        ; 0168      0000 0001 0110 1000
                        ; FFE0 and- 1111 1111 1110 0000
            and di, 0FFE0h                  ; else ┐ di &= E0 为了让它指向本条目开头
            add di, 20h                     ;      │
            mov si, offset LoaderFileName   ;      ├ di += 20h  下一个目录条目
            jmp LABEL_SEARCH_FOR_LOADERBIN  ;      ┘
            ;3333-搜寻根目录表项-1磁区16个档案记录(FDB)-0333333333333333333333333333333

;111-搜寻根目录表项 14 个-1111111111111111111111111111111111111111111111111111111111111
LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
    add     wSectorNo, 1
    jmp     LABEL_SEARCH_IN_ROOT_DIR_BEGIN
;111-搜寻根目录表项 14 个-1111111111111111111111111111111111111111111111111111111111111

LABEL_NO_LOADERBIN:
    mov     dh, 2           ; "No LOADER."
    call    DispStr         ; 显示字符串

    mov     ax, 4c00h       ; ┐
    int     21h             ; ┘没有找到 LOADER.BIN, 回到 DOS
;1111111111111111111111111111111111111111111111111111111111111111

LABEL_FILENAME_FOUND:       ; 找到 LOADER.BIN 后便来到这里继续
@@:
    jmp @B                  ; 代码暂时停在这里

;----------------------------------------------------------------------------
; 函数名: DispStr
;----------------------------------------------------------------------------
; 作用:
;   显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)

DispStr:
    jmp     @F
    BootMessage     db  "Booting  " ; 9字节, 不够则用空格补齐. 序号 0
    Message1        db  "Ready.   " ; 9字节, 不够则用空格补齐. 序号 1
    Message2        db  "No LOADER" ; 9字节, 不够则用空格补齐. 序号 2
@@:

    mov ax, 9
    mul dh
    add ax, offset BootMessage
    mov bp, ax              ; ┐
    mov ax, ds              ; ├ ES:BP = 串地址
    mov es, ax              ; ┘
    mov cx, 9               ; CX = 串长度
    mov ax, 01301h          ; AH = 13,  AL = 01h
    mov bx, 0007h           ; 页号为0(BH = 0) 黑底白字(BL = 07h)
    mov dl, 0
    int 10h                 ; 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     sp, 2              ; 辟出两个字节的堆栈区域保存要读的扇区数: byte [bp-2]

    mov     byte ptr [bp-2], cl
    push    bx                          ; 保存 bx
    mov     bl, byte ptr 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, byte ptr BS_DrvNum      ; 驱动器号 (0 表示 A 盘)
GoOnReading:
    mov ah, 2                           ; 读
    mov al, byte ptr [bp-2]             ; 读 al 个扇区
    int 13h
    jc  GoOnReading                     ; 如果读取错误 CF 会被置为 1,
                                        ; 这时就不停地读, 直到正确为止

    add sp, 2
    pop bp

    ret

;----------------------------------------------------------------------------
    org     start+01feh
    sign    dw  0aa55h

end start


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 5
支持
分享
最新回复 (2)
雪    币: 290
活跃值: (41)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
2
完成了 -> 载入 loader 至 9000:0100 -> 跳至 9000:0100 继续执行

;jmp BaseOfLoader:OffsetOfLoader
        mov ax, BaseOfLoader
        mov es, ax                          ; es <- BaseOfLoader
        mov bx, OffsetOfLoader              ; es:bx = BaseOfLoader:OffsetOfLoader

        push    es
        push    bx
        retf
    ;jmp BaseOfLoader:OffsetOfLoader

------------------------------------------------------------------------------------------------------------------
        .model tiny
        .code
    org    100h

BaseOfStack             equ 0100h   ; 调试状态下堆栈基地址(栈底, 从这个位置向低地址生长)
BaseOfLoader            equ 09000h  ; LOADER.BIN 被加载到的位置 ----  段地址
OffsetOfLoader          equ 0100h   ; LOADER.BIN 被加载到的位置 ---- 偏移地址
SectorNoOfFAT1          equ 1       ; FAT1 的第一个扇区号   = BPB_RsvdSecCnt
RootDirSectors          equ 14      ; 根目录占用空间
SectorNoOfRootDirectory equ 19      ; 根目录开始的磁区号
DeltaSectorNo           equ 17      ; DeltaSectorNo =
                                    ; Boot 占用磁区 + (FAT 数目 * 每FAT磁区数) - 2
    ; 数据开始磁区号 = 根目录开始的磁区号 + 根目录占用磁区 + (FAT 序号 - 2)

start:
    jmp short begin                 ; 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 0F0h         ; 媒体描述符
    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个字节

begin:
    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

    mov     dh, 0           ; "Booting  "
    call    DispStr         ; 显示字符串

    xor     ah, ah      ; ┐
    xor     dl, dl      ; ├ 软驱复位
    int     13h         ; ┘

    jmp     @F
        wSectorNo           dw      ?
        wRootDirSizeForLoop dw      ?       ; Root Directory 占用的扇区数,
    @@:                                     ; 在循环中会递减至零.
    mov     ax,SectorNoOfRootDirectory      ; 根目录的第一个磁区号 19
    mov     wSectorNo, ax
    mov     ax,RootDirSectors               ; 根目录占用空间 14
    mov     wRootDirSizeForLoop,ax
;111-搜寻根目录表项 14 个-1111111111111111111111111111111111111111111111111111111111111
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
    mov     ax,wRootDirSizeForLoop
    cmp     ax, 0                       ; ┐
    jz      LABEL_NO_LOADERBIN          ; ├ 判断根目录区是不是已经读完
    dec     ax                          ; ┘ 如果读完表示没有找到 LOADER.BIN
    mov     wRootDirSizeForLoop,ax
;111-搜寻根目录 14 个磁区-1111111111111111111111111111111111111111111111111111111111111

        ;2222-将 cl(1) 个根目录磁区读入 es:bx 中-22222222222222222222222222222222222222
        mov ax, BaseOfLoader
        mov es, ax                          ; es <- BaseOfLoader
        mov bx, OffsetOfLoader              ; es:bx = BaseOfLoader:OffsetOfLoader
        mov ax, wSectorNo                   ; ax <- Root Directory 中的某 Sector 号
        mov cl, 1
        call    ReadSector
        ;2222-将 cl(1) 个根目录磁区读入 es:bx 中-22222222222222222222222222222222222222

            ;3333-搜寻根目录表项-1磁区16个档案记录(FDB)-0333333333333333333333333333333
            jmp @F
                LoaderFileName      db  "LOADER  BIN", 0
            @@:
            mov si, offset LoaderFileName   ; ds:si -> "LOADER  BIN"
            mov di, OffsetOfLoader          ; es:di -> BaseOfLoader:0100
            cld                             ;当DF=0时,变址寄存器SI(和DI)增加1、2或4
            mov dx, 10h             ; 1磁区=512 Bytes , 512/32=16=10h 个档案记录(FDB)
        LABEL_SEARCH_FOR_LOADERBIN:
            cmp dx, 0                               ; ┐循环次数控制,
            jz  LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR  ; ├如果已经读完了一个 Sector,
            dec dx                                  ; ┘就跳到下一个 Sector
            ;3333-搜寻根目录表项-1磁区16个档案记录(FDB)-0333333333333333333333333333333

                ;4444-比对"LOADER  BIN"-44444444444444444444444444444444444444444444444
                mov cx, 11
            LABEL_CMP_FILENAME:
                cmp cx, 0
                jz  LABEL_FILENAME_FOUND    ; 如果比较了 11 个字符都相等, 表示找到
                dec cx
                lodsb                       ; ds:si -> al
                cmp al, byte ptr es:[di]
                jz  LABEL_GO_ON
                jmp LABEL_DIFFERENT         ; 只要发现不一样的字符就表明此根目录表项
                                            ; 不是我们要找的 LOADER.BIN
            LABEL_GO_ON:
                inc di
                jmp LABEL_CMP_FILENAME      ; 继续循环
                ;4444-比对"LOADER  BIN"-44444444444444444444444444444444444444444444444

            ;3333-搜寻根目录表项-1磁区16个档案记录(FDB)-0333333333333333333333333333333
        LABEL_DIFFERENT:
                        ; 32 = 20h = 2 ^5        1 2345 (此 5 位元清 0 )
                        ; 0168      0000 0001 0110 1000
                        ; FFE0 and- 1111 1111 1110 0000
            and di, 0FFE0h                  ; else ┐ di &= E0 为了让它指向本条目开头
            add di, 20h                     ;      │
            mov si, offset LoaderFileName   ;      ├ di += 20h  下一个目录条目
            jmp LABEL_SEARCH_FOR_LOADERBIN  ;      ┘
            ;3333-搜寻根目录表项-1磁区16个档案记录(FDB)-0333333333333333333333333333333

;111-搜寻根目录表项 14 个-1111111111111111111111111111111111111111111111111111111111111
LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
    add     wSectorNo, 1
    jmp     LABEL_SEARCH_IN_ROOT_DIR_BEGIN
;111-搜寻根目录表项 14 个-1111111111111111111111111111111111111111111111111111111111111

LABEL_NO_LOADERBIN:
    mov     dh, 2           ; "No LOADER."
    call    DispStr         ; 显示字符串

    mov     ax, 4c00h       ; ┐
    int     21h             ; ┘没有找到 LOADER.BIN, 回到 DOS

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

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

LABEL_FILENAME_FOUND:           ; 找到 LOADER.BIN 后便来到这里继续
                                ; es:di 为根目录 LOADER.BIN 表项
                                ; 例 9000:014B -> 0140 + 11d
;1111- 计算第一个数据开始的磁区号 -1111111111111111111111111111111111111111111111111111
    and     di, 0FFE0h          ; di -> 当前条目的开始 -> 9000:0140
    add     di, 01Ah            ; di -> 根目录指向 FAT -> 数据开始磁丛
    mov     cx, es:[di]
    push    cx                  ; 保存此"磁丛"在 FAT 中的序号
                                ; 以此获得 FAT 内容(指向下一个 FAT 序号)

        mov     ax, RootDirSectors  ; 根目录占用空间 14
        add     cx, ax
        add     cx, DeltaSectorNo   ; (BOOT + 2个FAT占用磁区)-2 -> 19-2=17
    ; 数据开始磁区号 = 根目录开始的磁区号 + 根目录占用磁区 + (FAT 序号 - 2)

        ;  cl 里面变成 LOADER.BIN 的数据起始磁区号 (从 0 开始数的序号)

;1111- 计算第一个数据开始的磁区号 -1111111111111111111111111111111111111111111111111111

    ;2222-  开始载入数据 从 9000:0100 开始一次载入 512 Bytes -2222222222222222222222222
        mov ax, BaseOfLoader
        mov es, ax
        mov bx, OffsetOfLoader      ; es:bx = 9000:0100
        mov ax, cx                  ; ax <- 第一个数据开始磁区号

    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
    ;2222-  开始载入数据 从 9000:0100 开始一次载入 512 Bytes -2222222222222222222222222

        ;3333- 取出 FAT 内容 -> 指向下一个 FAT 序号, 判断是否数据结束 -3333333333333333
            pop     ax              ; 取出此 磁区 在 FAT 中的序号
            call    GetFATEntry     ; 取出 FAT 内容 -> 指向下一个 FAT 序号
            cmp     ax, 0FFFh
            jz      LABEL_FILE_LOADED
            push    ax              ; 保存 磁区 在 FAT 中的序号
        ;3333- 取出 FAT 内容 -> 指向下一个 FAT 序号, 判断是否数据结束 -3333333333333333

    ;2222-  开始载入数据 从 9000:0100 开始一次载入 512 Bytes -2222222222222222222222222
        mov     dx, RootDirSectors          ; ┐
        add     ax, dx                      ; │ ax -> 下一个数据开始的 FAT 序号
        add     ax, DeltaSectorNo           ; ┘

                ;每磁丛占多少磁区 x 每磁区字节数
        add     bx, BPB_BytsPerSec          ; es:bx 增加一个 "磁丛"
        jmp     LABEL_GOON_LOADING_FILE
    ;2222-  开始载入数据 从 9000:0100 开始一次载入 512 Bytes -2222222222222222222222222

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

    ;jmp BaseOfLoader:OffsetOfLoader
        mov ax, BaseOfLoader
        mov es, ax                          ; es <- BaseOfLoader
        mov bx, OffsetOfLoader              ; es:bx = BaseOfLoader:OffsetOfLoader

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

;----------------------------------------------------------------------------
; 函数名: DispStr
;----------------------------------------------------------------------------
; 作用:
;   显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)

DispStr:
    jmp     @F
    BootMessage     db  "Booting  " ; 9字节, 不够则用空格补齐. 序号 0
    Message1        db  "Ready.   " ; 9字节, 不够则用空格补齐. 序号 1
    Message2        db  "No LOADER" ; 9字节, 不够则用空格补齐. 序号 2
@@:

    mov ax, 9
    mul dh
    add ax, offset BootMessage
    mov bp, ax              ; ┐
    mov ax, ds              ; ├ ES:BP = 串地址
    mov es, ax              ; ┘
    mov cx, 9               ; CX = 串长度
    mov ax, 01301h          ; AH = 13,  AL = 01h
    mov bx, 0007h           ; 页号为0(BH = 0) 黑底白字(BL = 07h)
    mov dl, 0
    int 10h                 ; 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     sp, 2              ; 辟出两个字节的堆栈区域保存要读的扇区数: byte [bp-2]

    mov     byte ptr [bp-2], cl
    push    bx                          ; 保存 bx

;   磁区号(ax)      ┌ 商 y (al)
; -------------- => ┤
;  每磁道磁区数     └ 余 z (ah)
    mov     bl, byte ptr BPB_SecPerTrk  ; BPB_SecPerTrk   DW 18 每磁道扇区数
    div     bl                          ; y 在 al 中, z 在 ah 中

; 起始磁区号 = 余 z (ah) + 1
    inc     ah                          ; z ++
    mov     cl, ah                      ; cl <- 起始扇区号

; 磁轨号 = 商 y (al) >> 1
    mov     dh, al                      ; dh <- y
    shr     al, 1                       ; y >> 1 (其实是 y/BPB_NumHeads, 这里BPB_NumHeads=2)
    mov     ch, al                      ; ch <- 磁轨号

; 磁头号 = 商 y (al) & 1
    and     dh, 1                       ; dh & 1 = 磁头号

; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^

    pop     bx                          ; 恢复 bx
    mov     dl, byte ptr BS_DrvNum      ; 驱动器号 (0 表示 A 盘)
GoOnReading:
    mov ah, 2                           ; 读
    mov al, byte ptr [bp-2]             ; 读 al 个扇区
    int 13h
    jc  GoOnReading                     ; 如果读取错误 CF 会被置为 1,
                                        ; 这时就不停地读, 直到正确为止

    add sp, 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

        jmp @F
            bOdd            db  0       ; 奇数还是偶数
        @@:
;== (FAT 序号 * 3/2) - 取得从 FAT 开始处偏移量 ========================================
;====== (从 FAT 开始处偏移量)/512 -> 取得相对于 FAT 来说的扇区号 ======================
;========== 读取正确扇区 -> (相对于 FAT 来说的扇区号)+(FAT1 的第一个扇区号) ===========
;============== FAT 序号指向的位址 -> (正确扇区)+(FAT 序号在此扇区内的偏移)============
;================== 取得 FAT 内容 计算 FAT 序号 =======================================

    ;1111- (FAT 序号) * 3/2 -> 取得从 FAT 开始处偏移量 -1111111111111111111111111111111
        mov     bx, 3       ; ┐    ax -> FAT 序号
        mul     bx          ; │    (占用 12 bits) 1.5 Bytes 即3/2
        mov     bx, 2       ; │    ax * 1.5 -> 从 FAT 开始处偏移量
        div     bx          ; ┘    (FAT占用不止一个扇区) 商有可能 > 512
            ; ax <- 商(从 FAT 开始处偏移量), dx <- 余数(0 或 1)
    ;1111- (FAT 序号) * 3/2 -> 取得从 FAT 开始处偏移量 -1111111111111111111111111111111

        cmp     dx, 0
        jz      LABEL_EVEN

    mov     bOdd, 1

LABEL_EVEN:     ;偶数
    ;2222- (从 FAT 开始处偏移量)/512 -> 取得相对于 FAT 来说的扇区号 -222222222222222222
        xor dx, dx
        mov bx, BPB_BytsPerSec  ; ┐ ax -> 从 FAT 开始处偏移量
        div bx                  ; ┘ ax / 512 -> 相对于 FAT 来说的扇区号
            ;   ax <- 商   (相对于 FAT 来说的扇区号)
            ;   dx <- 余数 (在此扇区内的偏移)。
    ;2222- (从 FAT 开始处偏移量)/512 -> 取得相对于 FAT 来说的扇区号 -222222222222222222

    push    dx

    ; ============================================
    mov     bx, 0               ; es:bx -> (9000-100):0000
    add     ax, SectorNoOfFAT1  ; (ax 相对于 FAT 来说的扇区号)+(FAT1 的第一个扇区号)
    mov     cl, 2               ; 读取 FATEntry 所在的扇区, 一次读两个,
    call    ReadSector          ; 避免在边界发生错误, 因为一个 FATEntry
                                ; 可能跨越两个扇区
    ; ============================================

        pop     dx              ; dx <- 余数 (在此扇区内的偏移)。
        add     bx, dx
        mov     ax, es:[bx]     ; ax -> FAT 序号指向的位址

        ;===== 取得 FAT 内容 计算 FAT 序号=======================================
        cmp     bOdd, 1         ; ┐
        jnz     LABEL_EVEN_2    ; │    (FAT 序号) * 3/2
        mov     cl, 4           ; │
        shr     ax, cl          ; │    有余数  (奇) -> 004F ->  右移 4   -> 0004
    LABEL_EVEN_2:               ; │
        and     ax, 0FFFh       ; ┘    没有余数(偶) -> F005 -> and 0FFFh ->0005
        ;===== 取得 FAT 内容 计算 FAT 序号=======================================

LABEL_GET_FAT_ENRY_OK:

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

    org     start+01feh
    sign    dw  0aa55h

end start
2013-7-17 15:46
0
雪    币: 65
活跃值: (545)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
贴个相关资料

http://bbs.pediy.com/showthread.php?t=173869
2013-7-19 09:46
0
游客
登录 | 注册 方可回帖
返回
//