boot.asm 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. ;将程序开始位置设置为0x7c00处,并给BaseOfStack赋值为0x7c00
  2. org 0x7c00
  3. BaseOfStack equ 0x7c00
  4. BaseOfLoader equ 0x1000
  5. OffsetOfLoader equ 0x00
  6. RootDirSectors equ 14 ;根目录占用的扇区数
  7. SectorNumOfRootDirStart equ 19 ; 根目录的起始扇区号
  8. SectorNumOfFAT1Start equ 1 ; FAT1表的起始扇区号 (因为前面有一个保留扇区(引导扇区))
  9. SectorBalance equ 17 ;平衡文件/目录的起始簇号与数据区域的起始簇号的差值。
  10. jmp short Label_Start
  11. nop
  12. BS_OEMName db 'DragonOS'
  13. BPB_BytesPerSec dw 512
  14. BPB_SecPerClus db 1
  15. BPB_RsvdSecCnt dw 1
  16. BPB_NumFATs db 2
  17. BPB_RootEntCnt dw 224
  18. BPB_TotSec16 dw 2880
  19. BPB_Media db 0xf0
  20. BPB_FATSz16 dw 9
  21. BPB_SecPerTrk dw 18
  22. BPB_NumHeads dw 2
  23. BPB_HiddSec dd 0
  24. BPB_TotSec32 dd 0
  25. BS_DrvNum db 0
  26. BS_Reserved1 db 0
  27. BS_BootSig db 0x29
  28. BS_VolID dd 0
  29. BS_VolLab db 'boot loader'
  30. BS_FileSysType db 'FAT12 '
  31. Label_Start:
  32. ;初始化寄存器
  33. mov ax, cs
  34. mov ds, ax
  35. mov es, ax
  36. mov ss, ax
  37. mov sp, BaseOfStack
  38. ;清屏
  39. mov ax, 0x0600 ;AL=0时,清屏,BX、CX、DX不起作用
  40. mov bx, 0x0700 ;设置白色字体,不闪烁,字体正常亮度,黑色背景
  41. mov cx, 0
  42. mov dx, 0184fh
  43. int 0x10
  44. ;设置屏幕光标位置为左上角(0,0)的位置
  45. mov ax, 0x0200
  46. mov bx, 0x0000
  47. mov dx, 0x0000
  48. int 10h
  49. ;在屏幕上显示Start Booting
  50. mov ax, 0x1301 ;设置显示字符串,显示后,光标移到字符串末端
  51. mov bx, 0x000a ;设置黑色背景,白色字体,高亮度,不闪烁
  52. mov dx, 0x0000 ;设置游标行列号均为0
  53. mov cx, 24 ;设置字符串长度为24
  54. push ax
  55. mov ax, ds
  56. mov es, ax
  57. pop ax
  58. mov bp, StartBootMessage
  59. int 0x10
  60. ;软盘驱动器复位
  61. xor ah, ah
  62. xor dl, dl
  63. int 0x13
  64. ; 在文件系统中搜索 loader.bin
  65. mov word [SectorNo], SectorNumOfRootDirStart ;保存根目录起始扇区号
  66. Label_Search_In_Root_Dir_Begin:
  67. cmp word [RootDirSizeForLoop], 0 ; 比较根目录扇区数量和0的关系。 cmp实际上是进行了一个减法运算
  68. jz Label_No_LoaderBin ; 等于0,不存在Loader.bin
  69. dec word [RootDirSizeForLoop]
  70. mov ax, 0x00
  71. mov es, ax
  72. mov bx, 0x8000
  73. mov ax, [SectorNo] ;向函数传入扇区号
  74. mov cl, 1
  75. call Func_ReadOneSector
  76. mov si, LoaderFileName ;向源变址寄存器传入Loader文件的名字
  77. mov di, 0x8000
  78. cld ;由于LODSB的加载方向与DF标志位有关,因此需要用CLD清零DF标志位
  79. mov dx, 0x10 ; 每个扇区的目录项的最大条数是(512/32=16,也就是0x10)
  80. Label_Search_For_LoaderBin:
  81. cmp dx, 0
  82. jz Label_Goto_Next_Sector_In_Root_Dir
  83. dec dx
  84. mov cx, 11 ; cx寄存器存储目录项的文件名长度, 11B,包括了文件名和扩展名,但是不包括 分隔符'.'
  85. Label_Cmp_FileName:
  86. cmp cx, 0
  87. jz Label_FileName_Found
  88. dec cx
  89. lodsb ; 把si对应的字节载入al寄存器中,然后,由于DF为0,si寄存器自增
  90. cmp al, byte [es:di] ; 间接取址[es+di]。 也就是进行比较当前文件的名字对应字节和loader文件名对应字节
  91. jz Label_Go_On ; 对应字节相同
  92. jmp Label_Different ; 字节不同,不是同一个文件
  93. Label_Go_On:
  94. inc di
  95. jmp Label_Cmp_FileName
  96. Label_Different:
  97. and di, 0xffe0 ;将di恢复到当前目录项的第0字节
  98. add di, 0x20 ;将di跳转到下一目录项的第0字节
  99. mov si, LoaderFileName
  100. jmp Label_Search_For_LoaderBin ;继续搜索下一目录项
  101. Label_Goto_Next_Sector_In_Root_Dir:
  102. add word [SectorNo], 1
  103. jmp Label_Search_In_Root_Dir_Begin
  104. Label_No_LoaderBin:
  105. ; 在屏幕上显示 [ERROR] No Loader Found.
  106. mov ax, 0x1301
  107. mov bx, 0x000c ; 红色闪烁高亮黑底
  108. mov dx, 0x0100 ; 显示在第二行(前面已经显示过一行了)
  109. mov cx, 24 ; 字符串长度
  110. push ax
  111. mov ax, ds
  112. mov es, ax
  113. pop ax
  114. mov bp, NoLoaderMessage
  115. int 0x10
  116. jmp $
  117. ;========== 找到了Loader.Bin
  118. Label_FileName_Found:
  119. mov ax, RootDirSectors
  120. ; 先取得目录项DIR_FstClus字段的值(起始簇号)
  121. and di, 0xffe0
  122. add di, 0x1a
  123. mov cx, word [es:di]
  124. push cx
  125. add cx, ax
  126. add cx, SectorBalance
  127. mov ax, BaseOfLoader
  128. mov es, ax ;配置es和bx,指定loader.bin在内存中的起始地址
  129. mov bx, OffsetOfLoader
  130. mov ax, cx
  131. Label_Go_On_Loading_File:
  132. push ax
  133. push bx
  134. ; 显示字符.
  135. mov ah, 0x0e
  136. mov al, "."
  137. mov bl, 0x0f
  138. int 0x10
  139. pop bx
  140. pop ax
  141. ; 每读取一个扇区,就获取下一个表项,然后继续读入下一个簇的数据,直到返回的下一表项为0xfff为止,表示loader.bin完全加载完成
  142. mov cl, 1
  143. call Func_ReadOneSector
  144. pop ax
  145. call Func_GetFATEntry
  146. cmp ax, 0xfff
  147. jz Label_File_Loaded
  148. push ax
  149. mov dx, RootDirSectors
  150. add ax, dx
  151. add ax, SectorBalance
  152. add bx, [BPB_BytesPerSec]
  153. jmp Label_Go_On_Loading_File
  154. Label_File_Loaded:
  155. ; 跳转到loader
  156. ; 这个指令结束后,目标段会复制到CS寄存器中
  157. jmp BaseOfLoader:OffsetOfLoader
  158. ; 从软盘读取一个扇区
  159. ; AX=待读取的磁盘起始扇区号
  160. ; CL=读入的扇区数量
  161. ; ES:BX=>目标缓冲区起始地址
  162. Func_ReadOneSector:
  163. push bp
  164. mov bp, sp
  165. sub esp, 2
  166. mov byte [bp-2], cl
  167. push bx
  168. mov bl, [BPB_SecPerTrk]
  169. div bl ;用AX寄存器中的值除以BL,得到目标磁道号(商:AL)以及目标磁道内的起始扇区号(余数:AH)
  170. inc ah ; 由于磁道内的起始扇区号从1开始计数,因此将余数+1
  171. mov cl, ah
  172. mov dh, al
  173. shr al, 1 ;计算出柱面号
  174. mov ch, al
  175. and dh, 1;计算出磁头号
  176. pop bx
  177. mov dl, [BS_DrvNum]
  178. ;最终,dh存储了磁头号,dl存储驱动器号
  179. ; ch存储柱面号,cl存储起始扇区号
  180. Label_Go_On_Reading:
  181. ; 使用BIOS中断服务程序INT13h的主功能号AH=02h实现软盘读取操作
  182. mov ah, 2
  183. mov al, byte [bp-2]
  184. int 0x13
  185. jc Label_Go_On_Reading ;当CF标志位被复位时,说明数据读取完成,恢复调用现场
  186. add esp, 2
  187. pop bp
  188. ret
  189. ; 解析FAT表项,根据当前FAT表项索引出下一个FAT表项
  190. Func_GetFATEntry:
  191. ; AX=FAT表项号(输入、输出参数)
  192. ; 保存将要被修改的寄存器
  193. push es
  194. push bx
  195. push ax
  196. ; 扩展段寄存器
  197. mov ax, 00
  198. mov es, ax
  199. pop ax
  200. mov byte [Odd], 0 ;将奇数标志位置0
  201. ; 将FAT表项号转换为总的字节号
  202. mov bx, 3
  203. mul bx
  204. mov bx, 2
  205. div bx
  206. cmp dx, 0
  207. jz Label_Even ; 偶数项
  208. mov byte [Odd], 1
  209. Label_Even:
  210. xor dx, dx ;把dx置0
  211. ; 计算得到扇区号(商)和扇区内偏移(余数)
  212. mov bx, [BPB_BytesPerSec]
  213. div bx
  214. push dx
  215. ; 读取两个扇区到[es:bx]
  216. mov bx, 0x8000
  217. add ax, SectorNumOfFAT1Start
  218. mov cl, 2 ; 设置读取两个扇区,解决FAT表项跨扇区的问题
  219. call Func_ReadOneSector
  220. pop dx
  221. add bx, dx
  222. mov ax, [es:bx]
  223. cmp byte [Odd], 1
  224. jnz Label_Even_2 ;若是偶数项,则跳转
  225. shr ax, 4 ; 解决奇偶项错位问题
  226. Label_Even_2:
  227. and ax, 0x0fff ; 确保表项号在正确的范围内 0x0003~0x0fff
  228. pop bx
  229. pop es
  230. ret
  231. ; 临时变量
  232. RootDirSizeForLoop dw RootDirSectors
  233. SectorNo dw 0
  234. Odd db 0
  235. ; 显示的文本
  236. StartBootMessage: db "[DragonOS] Start Booting"
  237. NoLoaderMessage: db "[ERROR] No LOADER Found."
  238. LoaderFileName: db "LOADER BIN",0 ;最后这个0是为了填满12字节的宽度
  239. ;填满整个扇区的512字节
  240. times 510 - ( $ - $$ ) db 0
  241. dw 0xaa55 ;===确保以0x55 0xaa为结尾