;将程序开始位置设置为0x7c00处,并给BaseOfStack赋值为0x7c00 org 0x7c00 BaseOfStack equ 0x7c00 BaseOfLoader equ 0x1000 OffsetOfLoader equ 0x00 RootDirSectors equ 14 ;根目录占用的扇区数 SectorNumOfRootDirStart equ 19 ; 根目录的起始扇区号 SectorNumOfFAT1Start equ 1 ; FAT1表的起始扇区号 (因为前面有一个保留扇区(引导扇区)) SectorBalance equ 17 ;平衡文件/目录的起始簇号与数据区域的起始簇号的差值。 jmp short Label_Start nop BS_OEMName db 'DragonOS' BPB_BytesPerSec dw 512 BPB_SecPerClus db 1 BPB_RsvdSecCnt dw 1 BPB_NumFATs db 2 BPB_RootEntCnt dw 224 BPB_TotSec16 dw 2880 BPB_Media db 0xf0 BPB_FATSz16 dw 9 BPB_SecPerTrk dw 18 BPB_NumHeads dw 2 BPB_HiddSec dd 0 BPB_TotSec32 dd 0 BS_DrvNum db 0 BS_Reserved1 db 0 BS_BootSig db 0x29 BS_VolID dd 0 BS_VolLab db 'boot loader' BS_FileSysType db 'FAT12 ' Label_Start: ;初始化寄存器 mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, BaseOfStack ;清屏 mov ax, 0x0600 ;AL=0时,清屏,BX、CX、DX不起作用 mov bx, 0x0700 ;设置白色字体,不闪烁,字体正常亮度,黑色背景 mov cx, 0 mov dx, 0184fh int 0x10 ;设置屏幕光标位置为左上角(0,0)的位置 mov ax, 0x0200 mov bx, 0x0000 mov dx, 0x0000 int 10h ;在屏幕上显示Start Booting mov ax, 0x1301 ;设置显示字符串,显示后,光标移到字符串末端 mov bx, 0x000a ;设置黑色背景,白色字体,高亮度,不闪烁 mov dx, 0x0000 ;设置游标行列号均为0 mov cx, 24 ;设置字符串长度为24 push ax mov ax, ds mov es, ax pop ax mov bp, StartBootMessage int 0x10 ;软盘驱动器复位 xor ah, ah xor dl, dl int 0x13 ; 在文件系统中搜索 loader.bin mov word [SectorNo], SectorNumOfRootDirStart ;保存根目录起始扇区号 Label_Search_In_Root_Dir_Begin: cmp word [RootDirSizeForLoop], 0 ; 比较根目录扇区数量和0的关系。 cmp实际上是进行了一个减法运算 jz Label_No_LoaderBin ; 等于0,不存在Loader.bin dec word [RootDirSizeForLoop] mov ax, 0x00 mov es, ax mov bx, 0x8000 mov ax, [SectorNo] ;向函数传入扇区号 mov cl, 1 call Func_ReadOneSector mov si, LoaderFileName ;向源变址寄存器传入Loader文件的名字 mov di, 0x8000 cld ;由于LODSB的加载方向与DF标志位有关,因此需要用CLD清零DF标志位 mov dx, 0x10 ; 每个扇区的目录项的最大条数是(512/32=16,也就是0x10) Label_Search_For_LoaderBin: cmp dx, 0 jz Label_Goto_Next_Sector_In_Root_Dir dec dx mov cx, 11 ; cx寄存器存储目录项的文件名长度, 11B,包括了文件名和扩展名,但是不包括 分隔符'.' Label_Cmp_FileName: cmp cx, 0 jz Label_FileName_Found dec cx lodsb ; 把si对应的字节载入al寄存器中,然后,由于DF为0,si寄存器自增 cmp al, byte [es:di] ; 间接取址[es+di]。 也就是进行比较当前文件的名字对应字节和loader文件名对应字节 jz Label_Go_On ; 对应字节相同 jmp Label_Different ; 字节不同,不是同一个文件 Label_Go_On: inc di jmp Label_Cmp_FileName Label_Different: and di, 0xffe0 ;将di恢复到当前目录项的第0字节 add di, 0x20 ;将di跳转到下一目录项的第0字节 mov si, LoaderFileName jmp Label_Search_For_LoaderBin ;继续搜索下一目录项 Label_Goto_Next_Sector_In_Root_Dir: add word [SectorNo], 1 jmp Label_Search_In_Root_Dir_Begin Label_No_LoaderBin: ; 在屏幕上显示 [ERROR] No Loader Found. mov ax, 0x1301 mov bx, 0x000c ; 红色闪烁高亮黑底 mov dx, 0x0100 ; 显示在第二行(前面已经显示过一行了) mov cx, 24 ; 字符串长度 push ax mov ax, ds mov es, ax pop ax mov bp, NoLoaderMessage int 0x10 jmp $ ;========== 找到了Loader.Bin Label_FileName_Found: mov ax, RootDirSectors ; 先取得目录项DIR_FstClus字段的值(起始簇号) and di, 0xffe0 add di, 0x1a mov cx, word [es:di] push cx add cx, ax add cx, SectorBalance mov ax, BaseOfLoader mov es, ax ;配置es和bx,指定loader.bin在内存中的起始地址 mov bx, OffsetOfLoader mov ax, cx Label_Go_On_Loading_File: push ax push bx ; 显示字符. mov ah, 0x0e mov al, "." mov bl, 0x0f int 0x10 pop bx pop ax ; 每读取一个扇区,就获取下一个表项,然后继续读入下一个簇的数据,直到返回的下一表项为0xfff为止,表示loader.bin完全加载完成 mov cl, 1 call Func_ReadOneSector pop ax call Func_GetFATEntry cmp ax, 0xfff jz Label_File_Loaded push ax mov dx, RootDirSectors add ax, dx add ax, SectorBalance add bx, [BPB_BytesPerSec] jmp Label_Go_On_Loading_File Label_File_Loaded: ; 跳转到loader ; 这个指令结束后,目标段会复制到CS寄存器中 jmp BaseOfLoader:OffsetOfLoader ; 从软盘读取一个扇区 ; AX=待读取的磁盘起始扇区号 ; CL=读入的扇区数量 ; ES:BX=>目标缓冲区起始地址 Func_ReadOneSector: push bp mov bp, sp sub esp, 2 mov byte [bp-2], cl push bx mov bl, [BPB_SecPerTrk] div bl ;用AX寄存器中的值除以BL,得到目标磁道号(商:AL)以及目标磁道内的起始扇区号(余数:AH) inc ah ; 由于磁道内的起始扇区号从1开始计数,因此将余数+1 mov cl, ah mov dh, al shr al, 1 ;计算出柱面号 mov ch, al and dh, 1;计算出磁头号 pop bx mov dl, [BS_DrvNum] ;最终,dh存储了磁头号,dl存储驱动器号 ; ch存储柱面号,cl存储起始扇区号 Label_Go_On_Reading: ; 使用BIOS中断服务程序INT13h的主功能号AH=02h实现软盘读取操作 mov ah, 2 mov al, byte [bp-2] int 0x13 jc Label_Go_On_Reading ;当CF标志位被复位时,说明数据读取完成,恢复调用现场 add esp, 2 pop bp ret ; 解析FAT表项,根据当前FAT表项索引出下一个FAT表项 Func_GetFATEntry: ; AX=FAT表项号(输入、输出参数) ; 保存将要被修改的寄存器 push es push bx push ax ; 扩展段寄存器 mov ax, 00 mov es, ax pop ax mov byte [Odd], 0 ;将奇数标志位置0 ; 将FAT表项号转换为总的字节号 mov bx, 3 mul bx mov bx, 2 div bx cmp dx, 0 jz Label_Even ; 偶数项 mov byte [Odd], 1 Label_Even: xor dx, dx ;把dx置0 ; 计算得到扇区号(商)和扇区内偏移(余数) mov bx, [BPB_BytesPerSec] div bx push dx ; 读取两个扇区到[es:bx] mov bx, 0x8000 add ax, SectorNumOfFAT1Start mov cl, 2 ; 设置读取两个扇区,解决FAT表项跨扇区的问题 call Func_ReadOneSector pop dx add bx, dx mov ax, [es:bx] cmp byte [Odd], 1 jnz Label_Even_2 ;若是偶数项,则跳转 shr ax, 4 ; 解决奇偶项错位问题 Label_Even_2: and ax, 0x0fff ; 确保表项号在正确的范围内 0x0003~0x0fff pop bx pop es ret ; 临时变量 RootDirSizeForLoop dw RootDirSectors SectorNo dw 0 Odd db 0 ; 显示的文本 StartBootMessage: db "[DragonOS] Start Booting" NoLoaderMessage: db "[ERROR] No LOADER Found." LoaderFileName: db "LOADER BIN",0 ;最后这个0是为了填满12字节的宽度 ;填满整个扇区的512字节 times 510 - ( $ - $$ ) db 0 dw 0xaa55 ;===确保以0x55 0xaa为结尾