boot/boot.asm

来源:互联网 发布:魅蓝手机怎么设置网络 编辑:程序博客网 时间:2024/05/16 17:25
Code:
  1. ;By Marcus Xing   
  2. ;boot/boot.asm,程序必须小于等于510字节   
  3. ;加载LOADER.BIN,并把控制权交给LOADER   
  4.   
  5. ;----------------------------------------------------------------------调试的预处理   
  6.   
  7. ;%define    _BOOT_DEBUG_    ; 做 Boot Sector 时一定将此行注释掉!   
  8.                             ; 将此行打开后用 nasm Boot.asm -o Boot.com 做成一个.COM文件易于调试   
  9.   
  10. %ifdef  _BOOT_DEBUG_   
  11.     org  0100h              ; 调试状态, 做成 .COM 文件, 可调试   
  12. %else   
  13.     org  07c00h             ; Boot 状态, Bios 将把 Boot Sector 加载到 0:7C00 处并开始执行   
  14. %endif   
  15.   
  16. ;---------------------------------------------------------------------软盘头信息   
  17.     jmp short LABEL_START       ; Start to boot.   
  18.     nop                         ; 这个 nop 不可少   
  19.   
  20.     ; 下面是 FAT12 磁盘的头   
  21.     BS_OEMName      db 'MarcusX '       ; OEM String, 必须 8 个字节   
  22.     BPB_BytsPerSec  dw 512              ; 每扇区字节数   
  23.     BPB_SecPerClus  db 1                ; 每簇多少扇区   
  24.     BPB_RsvdSecCnt  dw 1                ; Boot 记录占用多少扇区   
  25.     BPB_NumFATs     db 2                ; 共有多少 FAT 表   
  26.     BPB_RootEntCnt  dw 224              ; 根目录文件数最大值   
  27.     BPB_TotSec16    dw 2880             ; 逻辑扇区总数   
  28.     BPB_Media       db 0xF0             ; 媒体描述符   
  29.     BPB_FATSz16     dw 9                ; 每FAT扇区数   
  30.     BPB_SecPerTrk   dw 18               ; 每磁道扇区数   
  31.     BPB_NumHeads    dw 2                ; 磁头数(面数)   
  32.     BPB_HiddSec     dd 0                ; 隐藏扇区数   
  33.     BPB_TotSec32    dd 0                ; wTotalSectorCount为0时这个值记录扇区数   
  34.     BS_DrvNum       db 0                ; 中断 13 的驱动器号   
  35.     BS_Reserved1    db 0                ; 未使用   
  36.     BS_BootSig      db 29h              ; 扩展引导标记 (29h)   
  37.     BS_VolID        dd 0                ; 卷序列号   
  38.     BS_VolLab       dd 'MarcusOs0.2'    ; 卷标, 必须 11 个字节   
  39.     BS_FileSysType  db 'FAT12   '       ; 文件系统类型, 必须 8个字节     
  40.        
  41. ;-------------------------------------------------------------------------宏信息   
  42.     Base_Of_Loader          equ 9000h   ;加载LOADER的段地址   
  43.     Offset_Of_Loader        equ 0100h   ;加载LOADER的偏移地址   
  44.        
  45.     Root_Dir_Begin_Sector   equ 19      ;根目录区的逻辑起始逻辑扇区   
  46.   
  47. ;-------------------------------------------------------------------CODE_SEGMENT   
  48. LABEL_START:   
  49.     mov ax,cs   
  50.     mov ds,ax   
  51.     mov ss,ax   
  52.     mov sp,7c00h       
  53.        
  54.     ;清屛   
  55.     mov ax,0600h   
  56.     mov bx,0700h   
  57.     xor cx,cx   
  58.     mov dx,0184fh   
  59.     int 10h    
  60.            
  61.     ;显示字符串Booting   
  62.     push    _sz_Booting_Message   
  63.     call    Disp_Str_In_Real_Mode   
  64.     add sp,2   
  65.        
  66.     ;es指向缓冲区的段地址   
  67.     mov ax,Base_Of_Loader   
  68.     mov es,ax   
  69.        
  70. LABEL_READ_NEXT_SECTOR:   
  71.     mov bx,Offset_Of_Loader                         ;bx指向缓冲区的偏移地址   
  72.     cmp byte [_b_Root_Dir_Search_For_Loop],0        ;比较循环变量是否为0   
  73.     je  LABEL_NO_FOUND                              ;为0代表没找到,跳转到相应的标号处理   
  74.     dec byte [_b_Root_Dir_Search_For_Loop]          ;尚未为0,循环变量自减1   
  75.        
  76.     ;读取当前根目录区扇区至缓冲区   
  77.     push    1   
  78.     mov al,byte [_b_Root_Dir_Sector_No]   
  79.     xor ah,ah   
  80.     push    ax   
  81.     call    Read_Sector   
  82.     add sp,4   
  83.        
  84.     inc byte [_b_Root_Dir_Sector_No]                ;定位到下一个根目录扇区,为下一次读做准备   
  85.     mov dx,16                                       ;一个扇区有16个FCB,要循环16次   
  86.        
  87. LABEL_GO_ON_NEXT_DIR_ITEM:   
  88.     cmp dx,0                                        ;判断是否为0   
  89.     je  LABEL_READ_NEXT_SECTOR                      ;为0就读下一个根目录扇区   
  90.     dec dx                                          ;dx自减1   
  91.        
  92.     mov cx,11                                       ;FCB中的文件名字段有11位,故循环变量为11   
  93.     mov si,_s_Name_Of_Loader                        ;si定位到要比较的字符串偏移处   
  94.        
  95. LABEL_GO_ON_CMP:   
  96.     cmp cx,0                                        ;判断比较计数器是否为0,为0表示比较成功   
  97.     je  LABEL_FOUNDED                               ;即找到LOADER.BIN,跳转到相应标号处理   
  98.     dec cx                                          ;cx自减1   
  99.        
  100.     mov al,[si]                                     ;ds:si指向比较字符串,赋给al   
  101.     cmp al,[es:bx]                                  ;es:bx指向当前FCB的文件名字段,比较两者   
  102.     je  LABEL_CMP_OK                                ;比较成功则进行下一次比较   
  103.     and bx,0ffe0h                                   ;不成功则把bx的低5位清零,因为一个FCB为32   
  104.     add bx,32                                       ;字节,再加32则定位到下一个FCB的文件名处   
  105.     jmp LABEL_GO_ON_NEXT_DIR_ITEM                   ;跳转,比较下一个FCB   
  106.        
  107. LABEL_CMP_OK:   
  108.     ;两个串的定位器都自增1   
  109.     inc si                                                                                 
  110.     inc bx   
  111.     jmp LABEL_GO_ON_CMP                             ;跳转下一次比较   
  112.        
  113.     ;没找到LOADER,跳转到这儿,显示完相应信息后死循环   
  114. LABEL_NO_FOUND:    
  115.     push    _sz_No_Loader_Message   
  116.     call    Disp_Str_In_Real_Mode   
  117.     add sp,2   
  118.     jmp $   
  119.   
  120.     ;找到了LOADER,跳转到这儿   
  121. LABEL_FOUNDED:   
  122.     and bx,0ffe0h                                   ;使es:bx指向找到的LOADER的FCB的起始处   
  123.     mov cx,[es:bx + 1ah]                            ;取得LOADER的相对于数据区的偏移扇区号   
  124.                                                     ;注意:2为数据区的第一个扇区   
  125.                                                                                                    
  126.     mov ax,cx                                                                              
  127.     mov bx,Offset_Of_Loader                         ;es:bx=9000h:0100h,准备读入一个数据扇区   
  128.        
  129. LABEL_GO_ON_LOADING:   
  130.     ;每从数据区读一个扇区则显示一个点   
  131.     push    _sz_Dot   
  132.     call    Disp_Str_In_Real_Mode   
  133.     add sp,2   
  134.   
  135.     add ax,31                                       ;得到要读取的数据扇区的逻辑地址   
  136.        
  137.     ;读一个数据扇区   
  138.     push    1   
  139.     push    ax   
  140.     call    Read_Sector   
  141.     add sp,4   
  142.        
  143.     ;得到当前数据扇区在FAT中的值   
  144.     push    cx   
  145.     call    Get_FAT_Entry   
  146.     add sp,2   
  147.        
  148.     ;判断有没有下一个扇区,有则根据得到的下一个数据相对扇区号继续读   
  149.     ;没有则可以跳转到LOADER了   
  150.     cmp ax,0fffh   
  151.     je  LABEL_START_LOADING   
  152.        
  153.     add bx,512                                      ;定位LOADER的加载偏移地址   
  154.     mov cx,ax   
  155.     jmp LABEL_GO_ON_LOADING                         ;跳转回去进行相应处理   
  156.        
  157.     ;LOADER数据全部加载完毕后跳转到此   
  158. LABEL_START_LOADING:   
  159.     ;打印准备信息   
  160.     push    _sz_Ready_Message   
  161.     call    Disp_Str_In_Real_Mode   
  162.     add sp,2   
  163.        
  164.     ;正式跳转到LOADER!   
  165.     jmp Base_Of_Loader:Offset_Of_Loader   
  166.   
  167. ;-------------------------------------------------------------------DATA_SECTION   
  168. LABEL_DATA:   
  169.     _b_Root_Dir_Sector_No           db  Root_Dir_Begin_Sector       ;根目录区起始逻辑扇区   
  170.     _b_Root_Dir_Search_For_Loop     db  14                          ;找LOADER循环次数,就   
  171.                                                                     ;是根目录区扇区个数   
  172.                                                                        
  173.     _b_Is_Odd                       db  0                           ;FAT ENTRY逻辑地址的奇或偶   
  174.     _w_Disp_Pos                     dw  0                           ;显示地址   
  175.        
  176.     ;一些用到的串   
  177.     _sz_Booting_Message             db  'Booting',0                        
  178.     _sz_Ready_Message               db  'Ready',0   
  179.     _sz_No_Loader_Message           db  'NoLoader',0   
  180.     _sz_Dot                         db  '.',0   
  181.   
  182.     _s_Name_Of_Loader               db  'LOADER  BIN'   
  183.   
  184. ;--------------------------------------------------------------------Read_Sector   
  185. Read_Sector:   
  186. ;C函数原型(实模式下调用,短调用):   
  187. ;void Read_Sector(byte16 first_sector_index,byte16 read_sector_number);   
  188. ;注意:   
  189. ;调用前es:bx必须指向缓冲区,ds指向数据区   
  190. ;逻辑扇区号转化为系统调用所需参数的公式如下:   
  191. ;相对扇区号/每磁道扇区号(18)的商Q,余数R   
  192. ;其中:柱面号=Q>>1,磁头号=Q&1,相对起始扇区号=R+1   
  193.   
  194. ;对应的系统调用API如下:   
  195. ;功能号ah=02  
  196. ;hal=要读的扇区数   
  197. ;ch=柱面(磁道)号   
  198. ;cl=起始扇区号   
  199. ;dh=磁头号,dl=驱动器号(0表示A盘)   
  200. ;es:bx缓冲区地址   
  201.   
  202.     push    bp   
  203.     mov bp,sp   
  204.     push    ax   
  205.     push    bx   
  206.     push    cx   
  207.     push    dx   
  208.        
  209.     push    bx                      ;暂存缓冲区偏移   
  210.     mov ax,[bp + 6]                 ;取得要读取的扇区数   
  211.     push    ax                      ;暂时保存之   
  212.     mov ax,[bp + 4]                 ;取得要读的逻辑扇区号   
  213.     mov bl,[BPB_SecPerTrk]     
  214.     div bl                          ;除以每柱面扇区数   
  215.     mov ch,al                                  
  216.     shr ch,1                        ;ch<-柱面号   
  217.     mov dh,al   
  218.     and dh,1                        ;dh<-磁头号   
  219.     mov cl,ah   
  220.     inc cl                          ;cl<-相对扇区号   
  221.     mov dl,0                        ;读A盘   
  222.     pop ax                          ;al<-要读的扇区数   
  223.     pop bx                          ;恢复缓冲区偏移   
  224.        
  225. .Go_On_Reading:   
  226.     mov ah,2                        ;ah<-功能号   
  227.     int 13h   
  228.     jc  .Go_On_Reading              ;如果cf=1,继续读   
  229.        
  230.     pop dx   
  231.     pop cx   
  232.     pop bx   
  233.     pop ax   
  234.        
  235.     pop bp   
  236.     ret   
  237. ;------------------------------------------------------------------Get_FAT_Entry   
  238. Get_FAT_Entry:   
  239. ;C函数原型(实模式下调用,短调用):   
  240. ;u16 Get_FAT_Entry(u16 Sector_No_In_Data_Area);   
  241. ;功能:   
  242. ;入口参数为数据区的相对扇区号,返回值为相应的FAT的值,以表示还有没有下一个扇区   
  243. ;ds指向数据区,在此函数中,es:bx=9000h-100h:0用来作为读FAT的缓冲区   
  244.     push    bp   
  245.     mov bp,sp   
  246.   
  247.     push    bx   
  248.     push    dx   
  249.     push    es   
  250.        
  251.     mov byte [_b_Is_Odd],0      ;作用为局部变量,标记相对FAT的条目索引   
  252.        
  253.     ;es指向9000h-100h   
  254.     mov ax,Base_Of_Loader   
  255.     sub ax,100h   
  256.     mov es,ax   
  257.        
  258.     ;1个FAT条目为12位,条目索引*3/2 = *1.5   
  259.     ;ax为相对FAT字节偏移   
  260.     mov ax,[bp + 4]   
  261.     mov bx,3   
  262.     mul bx   
  263.     mov bx,2   
  264.     div bx   
  265.        
  266.     ;判断余数是否为0,是的话为偶,跳转到   
  267.     ;对应标号,否的话把标记变量置1   
  268.     cmp dx,0   
  269.     je  LABEL_EVEN   
  270.     mov byte [_b_Is_Odd],1   
  271.        
  272. LABEL_EVEN:   
  273.     ;ax=当前条目在FAT的相对逻辑偏移扇区   
  274.     ;dx=当前条目相对当前扇区的字节偏移   
  275.     xor dx,dx   
  276.     mov bx,512   
  277.     div bx   
  278.        
  279.     add ax,1                            ;ax自增1,求得相对整个软盘的逻辑扇区偏移   
  280.        
  281.     ;读2个FAT扇区到缓冲区,一次读2个,   
  282.     ;因为FAT条目可能跨越2个扇区   
  283.     xor bx,bx   
  284.     push    2   
  285.     push    ax   
  286.     call    Read_Sector   
  287.     add sp,4   
  288.   
  289.     mov bx,dx                           ;es:bx指向要求的条目首字节   
  290.     mov ax,[es:bx]                      ;把首字节存到ax   
  291.     cmp byte [_b_Is_Odd],1              ;判断奇偶,奇偶有不同的处理方式   
  292.     jne LABEL_EVEN2   
  293.     shr ax,4                            ;奇数的处理方式   
  294.        
  295. LABEL_EVEN2:                            ;偶数的处理方式   
  296.     and ax,0fffh                        ;返回值放到ax中   
  297.        
  298. LABEL_DONE:   
  299.     pop es   
  300.     pop dx   
  301.     pop bx   
  302.   
  303.     pop bp   
  304.     ret   
  305. ;----------------------------------------------------------Disp_Str_In_Real_Mode   
  306. Disp_Str_In_Real_Mode:   
  307. ;C函数原型(实模式下调用,短调用):   
  308. ;void Disp_Str_In_Real_Mode(const char *p_sz_Str);   
  309. ;注意:   
  310. ;在变量_w_Disp_Pos处显示字符串,ds指向数据区,es的值在此函数中指向数据区   
  311.     push    bp   
  312.     mov bp,sp   
  313.        
  314.     push    ax   
  315.     push    bx   
  316.     push    cx   
  317.     push    dx   
  318.     push    di   
  319.     push    es   
  320.        
  321. ;此BIOS中断API   
  322. ;功能13H    
  323. ;功能描述:在Teletype模式下显示字符串   
  324. ;入口参数:AH=13H   
  325. ;BH=页码   
  326. ;BL=属性(若AL=00H或01H)   
  327. ;CX=显示字符串长度   
  328. ;(DH、DL)=坐标(行、列)   
  329. ;ES:BP=显示字符串的地址 AL=显示输出方式   
  330. ;0——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置不变   
  331. ;1——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置改变   
  332. ;2——字符串中含显示字符和显示属性。显示后,光标位置不变   
  333. ;3——字符串中含显示字符和显示属性。显示后,光标位置改变   
  334. ;出口参数:无   
  335.   
  336.     mov ax,ds   
  337.     mov es,ax      
  338.     mov bp,[bp + 4]                 ;取得要打印的字符串指针   
  339.        
  340.     mov ax,[_w_Disp_Pos]            ;得到显示地址   
  341.     mov bl,80   
  342.     div bl   
  343.     mov dh,al                       ;dh<-行号   
  344.     mov dl,ah                       ;dl<-列号   
  345.        
  346.     xor cx,cx                       ;字符串长度计数器   
  347.     mov di,bp                       ;做临时指针es:di指向字符串   
  348.        
  349. .1:   
  350.     mov al,byte [es:di]   
  351.     cmp al,0                        ;遇到0计数结束   
  352.     je  .2   
  353.     inc cx                          ;计数器自增1   
  354.     inc di                          ;临时指针自增1   
  355.     jmp .1   
  356.   
  357. .2:   
  358.     add word [_w_Disp_Pos],cx       ;显示地址加上字符串长度   
  359.     mov bx,0007h                    ;bh表示第0页,bl表示字符颜色   
  360.     mov ax,1301h                    ;al为输出方式1   
  361.        
  362.     int 10h   
  363.        
  364.     pop es   
  365.     pop di   
  366.     pop dx   
  367.     pop cx   
  368.     pop bx   
  369.     pop ax   
  370.        
  371.     pop bp   
  372.     ret   
  373.        
  374.     times   510 - ($ - $$) db 0   
  375.     dw  0aa55h                      ;引导扇区最后两个字节以0xaa55结束
原创粉丝点击