boot/boot.asm
来源:互联网 发布:魅蓝手机怎么设置网络 编辑:程序博客网 时间:2024/05/16 17:25
- ;By Marcus Xing
- ;boot/boot.asm,程序必须小于等于510字节
- ;加载LOADER.BIN,并把控制权交给LOADER
-
- ;----------------------------------------------------------------------调试的预处理
-
- ;%define _BOOT_DEBUG_ ; 做 Boot Sector 时一定将此行注释掉!
- ; 将此行打开后用 nasm Boot.asm -o Boot.com 做成一个.COM文件易于调试
-
- %ifdef _BOOT_DEBUG_
- org 0100h ; 调试状态, 做成 .COM 文件, 可调试
- %else
- org 07c00h ; Boot 状态, Bios 将把 Boot Sector 加载到 0:7C00 处并开始执行
- %endif
-
- ;---------------------------------------------------------------------软盘头信息
- jmp short LABEL_START ; Start to boot.
- nop ; 这个 nop 不可少
-
- ; 下面是 FAT12 磁盘的头
- BS_OEMName db 'MarcusX ' ; 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 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 dd 'MarcusOs0.2' ; 卷标, 必须 11 个字节
- BS_FileSysType db 'FAT12 ' ; 文件系统类型, 必须 8个字节
-
- ;-------------------------------------------------------------------------宏信息
- Base_Of_Loader equ 9000h ;加载LOADER的段地址
- Offset_Of_Loader equ 0100h ;加载LOADER的偏移地址
-
- Root_Dir_Begin_Sector equ 19 ;根目录区的逻辑起始逻辑扇区
-
- ;-------------------------------------------------------------------CODE_SEGMENT
- LABEL_START:
- mov ax,cs
- mov ds,ax
- mov ss,ax
- mov sp,7c00h
-
- ;清屛
- mov ax,0600h
- mov bx,0700h
- xor cx,cx
- mov dx,0184fh
- int 10h
-
- ;显示字符串Booting
- push _sz_Booting_Message
- call Disp_Str_In_Real_Mode
- add sp,2
-
- ;es指向缓冲区的段地址
- mov ax,Base_Of_Loader
- mov es,ax
-
- LABEL_READ_NEXT_SECTOR:
- mov bx,Offset_Of_Loader ;bx指向缓冲区的偏移地址
- cmp byte [_b_Root_Dir_Search_For_Loop],0 ;比较循环变量是否为0
- je LABEL_NO_FOUND ;为0代表没找到,跳转到相应的标号处理
- dec byte [_b_Root_Dir_Search_For_Loop] ;尚未为0,循环变量自减1
-
- ;读取当前根目录区扇区至缓冲区
- push 1
- mov al,byte [_b_Root_Dir_Sector_No]
- xor ah,ah
- push ax
- call Read_Sector
- add sp,4
-
- inc byte [_b_Root_Dir_Sector_No] ;定位到下一个根目录扇区,为下一次读做准备
- mov dx,16 ;一个扇区有16个FCB,要循环16次
-
- LABEL_GO_ON_NEXT_DIR_ITEM:
- cmp dx,0 ;判断是否为0
- je LABEL_READ_NEXT_SECTOR ;为0就读下一个根目录扇区
- dec dx ;dx自减1
-
- mov cx,11 ;FCB中的文件名字段有11位,故循环变量为11
- mov si,_s_Name_Of_Loader ;si定位到要比较的字符串偏移处
-
- LABEL_GO_ON_CMP:
- cmp cx,0 ;判断比较计数器是否为0,为0表示比较成功
- je LABEL_FOUNDED ;即找到LOADER.BIN,跳转到相应标号处理
- dec cx ;cx自减1
-
- mov al,[si] ;ds:si指向比较字符串,赋给al
- cmp al,[es:bx] ;es:bx指向当前FCB的文件名字段,比较两者
- je LABEL_CMP_OK ;比较成功则进行下一次比较
- and bx,0ffe0h ;不成功则把bx的低5位清零,因为一个FCB为32
- add bx,32 ;字节,再加32则定位到下一个FCB的文件名处
- jmp LABEL_GO_ON_NEXT_DIR_ITEM ;跳转,比较下一个FCB
-
- LABEL_CMP_OK:
- ;两个串的定位器都自增1
- inc si
- inc bx
- jmp LABEL_GO_ON_CMP ;跳转下一次比较
-
- ;没找到LOADER,跳转到这儿,显示完相应信息后死循环
- LABEL_NO_FOUND:
- push _sz_No_Loader_Message
- call Disp_Str_In_Real_Mode
- add sp,2
- jmp $
-
- ;找到了LOADER,跳转到这儿
- LABEL_FOUNDED:
- and bx,0ffe0h ;使es:bx指向找到的LOADER的FCB的起始处
- mov cx,[es:bx + 1ah] ;取得LOADER的相对于数据区的偏移扇区号
- ;注意:2为数据区的第一个扇区
-
- mov ax,cx
- mov bx,Offset_Of_Loader ;es:bx=9000h:0100h,准备读入一个数据扇区
-
- LABEL_GO_ON_LOADING:
- ;每从数据区读一个扇区则显示一个点
- push _sz_Dot
- call Disp_Str_In_Real_Mode
- add sp,2
-
- add ax,31 ;得到要读取的数据扇区的逻辑地址
-
- ;读一个数据扇区
- push 1
- push ax
- call Read_Sector
- add sp,4
-
- ;得到当前数据扇区在FAT中的值
- push cx
- call Get_FAT_Entry
- add sp,2
-
- ;判断有没有下一个扇区,有则根据得到的下一个数据相对扇区号继续读
- ;没有则可以跳转到LOADER了
- cmp ax,0fffh
- je LABEL_START_LOADING
-
- add bx,512 ;定位LOADER的加载偏移地址
- mov cx,ax
- jmp LABEL_GO_ON_LOADING ;跳转回去进行相应处理
-
- ;LOADER数据全部加载完毕后跳转到此
- LABEL_START_LOADING:
- ;打印准备信息
- push _sz_Ready_Message
- call Disp_Str_In_Real_Mode
- add sp,2
-
- ;正式跳转到LOADER!
- jmp Base_Of_Loader:Offset_Of_Loader
-
- ;-------------------------------------------------------------------DATA_SECTION
- LABEL_DATA:
- _b_Root_Dir_Sector_No db Root_Dir_Begin_Sector ;根目录区起始逻辑扇区
- _b_Root_Dir_Search_For_Loop db 14 ;找LOADER循环次数,就
- ;是根目录区扇区个数
-
- _b_Is_Odd db 0 ;FAT ENTRY逻辑地址的奇或偶
- _w_Disp_Pos dw 0 ;显示地址
-
- ;一些用到的串
- _sz_Booting_Message db 'Booting',0
- _sz_Ready_Message db 'Ready',0
- _sz_No_Loader_Message db 'NoLoader',0
- _sz_Dot db '.',0
-
- _s_Name_Of_Loader db 'LOADER BIN'
-
- ;--------------------------------------------------------------------Read_Sector
- Read_Sector:
- ;C函数原型(实模式下调用,短调用):
- ;void Read_Sector(byte16 first_sector_index,byte16 read_sector_number);
- ;注意:
- ;调用前es:bx必须指向缓冲区,ds指向数据区
- ;逻辑扇区号转化为系统调用所需参数的公式如下:
- ;相对扇区号/每磁道扇区号(18)的商Q,余数R
- ;其中:柱面号=Q>>1,磁头号=Q&1,相对起始扇区号=R+1
-
- ;对应的系统调用API如下:
- ;功能号ah=02
- ;hal=要读的扇区数
- ;ch=柱面(磁道)号
- ;cl=起始扇区号
- ;dh=磁头号,dl=驱动器号(0表示A盘)
- ;es:bx缓冲区地址
-
- push bp
- mov bp,sp
- push ax
- push bx
- push cx
- push dx
-
- push bx ;暂存缓冲区偏移
- mov ax,[bp + 6] ;取得要读取的扇区数
- push ax ;暂时保存之
- mov ax,[bp + 4] ;取得要读的逻辑扇区号
- mov bl,[BPB_SecPerTrk]
- div bl ;除以每柱面扇区数
- mov ch,al
- shr ch,1 ;ch<-柱面号
- mov dh,al
- and dh,1 ;dh<-磁头号
- mov cl,ah
- inc cl ;cl<-相对扇区号
- mov dl,0 ;读A盘
- pop ax ;al<-要读的扇区数
- pop bx ;恢复缓冲区偏移
-
- .Go_On_Reading:
- mov ah,2 ;ah<-功能号
- int 13h
- jc .Go_On_Reading ;如果cf=1,继续读
-
- pop dx
- pop cx
- pop bx
- pop ax
-
- pop bp
- ret
- ;------------------------------------------------------------------Get_FAT_Entry
- Get_FAT_Entry:
- ;C函数原型(实模式下调用,短调用):
- ;u16 Get_FAT_Entry(u16 Sector_No_In_Data_Area);
- ;功能:
- ;入口参数为数据区的相对扇区号,返回值为相应的FAT的值,以表示还有没有下一个扇区
- ;ds指向数据区,在此函数中,es:bx=9000h-100h:0用来作为读FAT的缓冲区
- push bp
- mov bp,sp
-
- push bx
- push dx
- push es
-
- mov byte [_b_Is_Odd],0 ;作用为局部变量,标记相对FAT的条目索引
-
- ;es指向9000h-100h
- mov ax,Base_Of_Loader
- sub ax,100h
- mov es,ax
-
- ;1个FAT条目为12位,条目索引*3/2 = *1.5
- ;ax为相对FAT字节偏移
- mov ax,[bp + 4]
- mov bx,3
- mul bx
- mov bx,2
- div bx
-
- ;判断余数是否为0,是的话为偶,跳转到
- ;对应标号,否的话把标记变量置1
- cmp dx,0
- je LABEL_EVEN
- mov byte [_b_Is_Odd],1
-
- LABEL_EVEN:
- ;ax=当前条目在FAT的相对逻辑偏移扇区
- ;dx=当前条目相对当前扇区的字节偏移
- xor dx,dx
- mov bx,512
- div bx
-
- add ax,1 ;ax自增1,求得相对整个软盘的逻辑扇区偏移
-
- ;读2个FAT扇区到缓冲区,一次读2个,
- ;因为FAT条目可能跨越2个扇区
- xor bx,bx
- push 2
- push ax
- call Read_Sector
- add sp,4
-
- mov bx,dx ;es:bx指向要求的条目首字节
- mov ax,[es:bx] ;把首字节存到ax
- cmp byte [_b_Is_Odd],1 ;判断奇偶,奇偶有不同的处理方式
- jne LABEL_EVEN2
- shr ax,4 ;奇数的处理方式
-
- LABEL_EVEN2: ;偶数的处理方式
- and ax,0fffh ;返回值放到ax中
-
- LABEL_DONE:
- pop es
- pop dx
- pop bx
-
- pop bp
- ret
- ;----------------------------------------------------------Disp_Str_In_Real_Mode
- Disp_Str_In_Real_Mode:
- ;C函数原型(实模式下调用,短调用):
- ;void Disp_Str_In_Real_Mode(const char *p_sz_Str);
- ;注意:
- ;在变量_w_Disp_Pos处显示字符串,ds指向数据区,es的值在此函数中指向数据区
- push bp
- mov bp,sp
-
- push ax
- push bx
- push cx
- push dx
- push di
- push es
-
- ;此BIOS中断API
- ;功能13H
- ;功能描述:在Teletype模式下显示字符串
- ;入口参数:AH=13H
- ;BH=页码
- ;BL=属性(若AL=00H或01H)
- ;CX=显示字符串长度
- ;(DH、DL)=坐标(行、列)
- ;ES:BP=显示字符串的地址 AL=显示输出方式
- ;0——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置不变
- ;1——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置改变
- ;2——字符串中含显示字符和显示属性。显示后,光标位置不变
- ;3——字符串中含显示字符和显示属性。显示后,光标位置改变
- ;出口参数:无
-
- mov ax,ds
- mov es,ax
- mov bp,[bp + 4] ;取得要打印的字符串指针
-
- mov ax,[_w_Disp_Pos] ;得到显示地址
- mov bl,80
- div bl
- mov dh,al ;dh<-行号
- mov dl,ah ;dl<-列号
-
- xor cx,cx ;字符串长度计数器
- mov di,bp ;做临时指针es:di指向字符串
-
- .1:
- mov al,byte [es:di]
- cmp al,0 ;遇到0计数结束
- je .2
- inc cx ;计数器自增1
- inc di ;临时指针自增1
- jmp .1
-
- .2:
- add word [_w_Disp_Pos],cx ;显示地址加上字符串长度
- mov bx,0007h ;bh表示第0页,bl表示字符颜色
- mov ax,1301h ;al为输出方式1
-
- int 10h
-
- pop es
- pop di
- pop dx
- pop cx
- pop bx
- pop ax
-
- pop bp
- ret
-
- times 510 - ($ - $$) db 0
- dw 0aa55h ;引导扇区最后两个字节以0xaa55结束