org 07c00h ;================================================ jmp short START nop ; 这个 nop 不可少 ;这个结构将要被写在软盘的第一个扇区,至关于格式化软盘为FAT12格式 BS_OEMName DB 'PAVKOOOO' ; OEM String, 必须 8 个字节 BPB_BytsPerSec DW 512 ; 每扇区512字节 BPB_SecPerClus DB 1 ; 每簇1扇区 簇的定义是为了操做系统可以更加快速的去硬盘寻址,快速定位,而不仅是使用扇区号 BPB_RsvdSecCnt DW 1 ; 引导区使用1个扇区,不能有其余值,由于BIOS启动以后就会去取软盘的第一个扇区全部数据到内存,而后将控制权交给这个引导区 BPB_NumFATs DB 2 ; 共有2 FAT 表 ;FAT格式(12,16,32)几乎都是2个表,第二个表用来备份。因此内容和第一个表彻底同样 BPB_RootEntCnt DW 224 ; 最多能存储224个文件??不清楚为何是这个值 BPB_TotSec16 DW 2880 ; 逻辑扇区总数:FAT12的扇区个数=2个磁头*18个磁道*80个扇区 =2880 BPB_Media DB 0xF0 ; 媒体描述符 ?? BPB_FATSz16 DW 9 ; 每FAT扇9个 BPB_SecPerTrk DW 18 ; 每磁道18个扇区 BPB_NumHeads DW 2 ; 磁头数(面数) BPB_HiddSec DD 0 ; 隐藏扇区数 BPB_TotSec32 DD 0 ; 总扇区数,若是前面16位已经记录,这个就为0 BS_DrvNum DB 0 ; 中断 13 的驱动器号 BS_Reserved1 DB 0 ; 未使用 BS_BootSig DB 29h ; 扩展引导标记 (29h) BS_VolID DD 0 ; 卷序列号 BS_VolLab DB 'PAVKOOOOOOO'; 卷标, 必须 11 个字节 BS_FileSysType DB 'FAT12 ' ; 文件系统类型, 必须 8个字节 BaseofStack equ 07c00h FileEntryBegin equ 19 CsOfLoader equ 010000h ;在实模式下,将loader.bin加载的内存地址应该为 07c00h+引导程序(512=200h)=07e00h以后 ipofLoader equ 0100h ;确保程序加载的内容在实模式寻址空间以内:1MB FileEntryCount equ 14 ;文件目录区所占用的扇区数 ;=============引导程序开始执行==================== START: xor eax,eax mov ax,cs mov ds,ax ;数据段,视频段,附加段都指向当前段 mov ss,ax mov sp,BaseofStack ;栈顶ss:sp ==> 07C00h 也就是程序开始的位置 ,栈往低地址生长,这里没有作堆栈溢出检查 ;复位软驱 xor ah,ah xor dl,dl int 13h ;在软盘中读取1个扇区到内存 ;由于loader.bin存在于根目录区,根目录区在扇区号19(0---引导扇区,1~9---FAT表1,10~18---FAT表2),因此须要将19扇区加载到内存 ;整个目录区占用了多少个扇区? 224(个文件)*32(每一个文件占用32个字节)/512(每一个扇区512个字节) = 14 占用了14个扇区。 mov LoopSecNo,FileEntryBegin BEGINREADSEC: cmp LoopOfSec,0 jz NOLOADER dec LoopOfSec mov ax,CsOfLoader mov es,ax mov ax,ipofLoader mov bx,ax ;将数据读到es:bx指向的缓存中 mov ax,LoopSecNo mov cl,1 ;只读1个扇区 call ReadSector ;读完以后,内存里就有512字节的内容了,如今仍是寻找loader.bin mov si, LoaderFileName ; ds:si -> "LOADER BIN" mov di, ipofLoader cld mov dx, 10h ;一个扇区512 / 32有16(10H)个目录项 BEGINLOOPFILENAME: ;开始一个个项目的比较 cmp dx, 0 jz NEXTSECTOR ;到下一个扇区寻找,因此须要从新加载一个扇区 dec dx ;11个字母的文件名 ; ┛就跳到下一个 Sector mov cx, 11 COMPARENAME: cmp cx, 0 jz FILEFOUND ;文件找到了 dec cx lodsb ; ds:si -> al cmp al, byte [es:di] jz NEXTCHAR jmp NEXTFILE NEXTCHAR: inc di jmp COMPARENAME NEXTFILE: and di, 0FFE0h loadsb会改变di,因此须要将di指定到文件条目的首地址 add di, 20h ;每一个文件条目占有32个字符=20H jmp BEGINLOOPFILENAME NEXTSECTOR: add LoopSecNo,1 jmp BEGINREADSEC NOLOADER: jmp $ ;找不到的话,就卡死到这里 ;若是文件找到了,就要把文件从数据区读取到内存 ;目录区以后就是数据区,因此数据区的第一个扇区号是19+14=33! FILEFOUND: and di, 0FFE0h ; di -> 当前条目的开始 add di, 01Ah ;01Ah = 26 也就是文件条目地址偏移26的位置:DRI_FSTCLUS ;文件所在的数据区扇区号为:dir.DIR_FileSize -2 + 33 ==> [es:di] mov word ax,[es:di]; push ax ;保存序号,getfatentry的时候须要使用 sub ax 2 add ax 33 LABEL_GOON_LOADING_FILE: mov cl,1 ReadSec ;loader.bin可能大于512字节,就是说可能超过1个扇区(不能超过两个,由于如今在实模式,实模式的最大寻址为1M) ;因此须要读取fat表,查看是否还有其余的扇区 pop ax call GetFATEntry cmp ax, 0FFFh jz FILELOADED ;读取下一个未完的扇区 sub ax 2 add ax 33 ;将数据读到es:bx指向的缓存中 add bx, [BPB_BytsPerSec] ; 读取的内存指针也应该后移512个字节(1个扇区) jmp LABEL_GOON_LOADING_FILE FILELOADED: ; ********************** ; 这一句正式跳转到已加载到内 ; 存中的 LOADER.BIN 的开始处, ; 开始执行 LOADER.BIN 的代码。 ; Boot Sector 的使命到此结束。 jmp CsofLoader:ipofLoader ; ********************** ;---------------------------------------------------------------------------- ; 函数名: GetFATEntry ;---------------------------------------------------------------------------- ; 做用: ; 找到序号DRI_FSTCLUS为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中 ; 须要注意的是, 中间须要读 FAT 的扇区到 es:bx 处, 因此函数一开始保存了 es 和 bx GetFATEntry: push es push bx push ax mov ax, CsOfLoader; `. 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 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;如何函数使用INT 13读取软盘 ;http://en.wikipedia.org/wiki/INT_13H ;传入扇区号到ax ;读取扇区数目到cl ;将数据读到es:bx指向的缓存中 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;在函数里面最好使用bp而不是使用SP ReadSec: push bp mov bp,sp sub sp,2 mov bype [bp-2],cl push bx mov bl,[BPB_SecPerTrk] div bl ;扇区号/18; ah=余数,al=商 inc ah ;扇区号从0开始,因此须要加1 mov cl,ah mov dh,al ;al表示磁道号 shr al,1 ;al / 2 的值为磁道号 mov ch,al and dh,1 ;dh磁头号 mov dl=[BS_DrvNum] pop bx ;int 13所须要的值都已经附了 LoopWhenFeild: mov ah,2 mov cl,[bp-2] int 13 jc LoopWhenFeild add sp,2 pop bp ret ;-.- -.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.--.-! ;变量定义区 LoaderFileName db "LOADER BIN", 0 ; LOADER.BIN 之文件名 LoopOfSec dw FileEntryCount; LoopSecNo dw 0; bOdd db 0 ; 奇数仍是偶数 ;---------------------------------------------------------------------------- times 510-($-$$) db 0 ; 填充剩下的空间,使生成的二进制代码刚好为512字节 dw 0xaa55 ; 结束标志