操作系统u盘启动扇区

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 不可少
BP_OEMName DB 'FXSTAR1 '
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 'AREA OS v01'; 卷标, 必须 11 个字节
BS_FileSysType DB 'FAT12   ' ; 文件系统类型, 必须 8个字节  

LABEL_START:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, BaseOfStack
call os_cls
call os_searchfile

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

;----------------------------------------------------------------------------
;AREA OS FUNCTION
os_cls:
mov ax, 0600h
mov bx, 0700h
mov cx, 0000h
mov dx, 184fh
int 10h
        mov     ah, 02h
        mov     bh, 00h
        mov dx, 00h
        int     10h
mov ax, 0
mov [ds:position_x], al
mov [ds:position_y], al
ret

os_message: ;输入参数:ax-数据起始地址
mov bx, ax
add ax, 2
mov     bp, ax                  ; ES:BP = 串地址
mov     cx, [es:bx] ; CX = 串长度
mov ax, 0
mov     ax, 1301h ; AH = 13,  AL = 01h
        mov     bx, 0007h               ; 页号为0(BH = 0) 黑底白字(BL = 07h)
mov dl, [ds:position_x] ; 列
        mov dh, [ds:position_y] ; 行
int     10h                     ; int 10h
mov bl, [ds:position_y]
add bx, 1
mov ax, cx
mov dl, 80
div dl
add bx, ax
mov [ds:position_y],bl
        ret

os_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: ;偶数
; 现在 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 cx, 2
call os_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

os_readsector: ;输入参数:ax-起始扇区位置 es:bx-传输缓冲区地址 cx-传输扇区数
mov [Data+8],ax
mov [Data+6],es
mov [Data+4],bx
mov [Data+2],cx
.GoOnReading: 
mov     si,Data
        mov     dl,80h
        mov     ah,42h
        int     13h
        jc      .GoOnReading            ; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到正确为止
        ret

os_searchfile:
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 cx, 1
call os_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 ax,0
mov es,ax
mov ax,Flag
call os_message
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-based)
mov ax, BaseOfLoader
mov es, ax ; es <- BaseOfLoader
mov bx, OffsetOfLoader ; bx <- OffsetOfLoader
mov ax, cx ; ax <- Sector 号

LABEL_GOON_LOADING_FILE:
mov cx, 1
call os_readsector
pop ax ; 取出此 Sector 在 FAT 中的序号
call os_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:
ret


;----------------------------------------------------------------------------
;BOOT DATA
LoaderFileName          db      "LOADER  BIN", 0        ; LOADER.BIN 之文件名

BootMessage: db 1,0
db "Boot"
LoadMessage: db 1,0
db "Load"
Flag: db 5,0
db "Flag!"

position_x: db 0h
position_y: db 0h
wRootDirSizeForLoop     dw      RootDirSectors  ; Root Directory 占用的扇区数, 在循环中会递减至零.
wSectorNo               dw      0               ; 要读取的扇区号
bOdd                    db      0               ; 奇数还是偶数

Data: db 10h
db 0
dw 01h
dd 00000100h
dd 0
dd 0

;----------------------------------------------------------------------------

times 510-($-$$) db 0 ; 填充剩下的空间,使生成的二进制代码恰好为512字节
dw 0xaa55 ; 结束标志

你可能感兴趣的:(汇编,操作系统,U盘,启动扇区)