欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

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

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

于渊自己动手写操作系统
自己用 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

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功