《操作系统真象还原》BIOS

  以下是读本书第二章的收获。


  记得我大学学习操作系统的时候会遇到一些奇奇怪怪的问题,因为觉得问题太奇怪了,所以羞于问老师。诸如ROM到底是个什么东西,是不是个硬盘呢;如果用内存映射的方式访问外部设备,是不是内存条里专门有块内存空间来用于访问供外部设备,是不是先访问内存条这个地址,然后就直接跳到访问这个设备了等等。幸运的是,这本书都给我一一解答了。

  实际上,ROM是下图这样的一种只读存储器(取自百度百科),是一种即使没有通电,也能保存信息的存储器。ROM其实是既可以读也可以写,只不过由于历史原因统称只读存储器。ROM种类不少,我们常用的固态硬盘就是基于闪存(一种ROM)的存储器。

  我们下面将要说的BIOS正是存储在ROM设备中的程序,为什么BIOS放在ROM上?个人觉得,BIOS每次通电开机时都要运行,所以是不适合RAM这种断电即丢失信息的存储器了;至于磁盘也不太适合了,我们知道CPU能够直接访问的只有寄存器和内存,不包括磁盘这种外围设备。所以要执行在磁盘中程序的话,首先要将代码加载到内存里,再让CPU从内存取指令出来执行,但因为此时还未执行BIOS程序,来建立对硬件IO操作的功能,所以将代码从磁盘里加载出来的功能还不能使用。这样的话,磁盘程序放不出BIOS,BIOS也执行不了初始化操作,将BIOS放在硬盘估计就是让BIOS和硬盘俩对着对方干瞪眼,所以硬盘并不适合。而ROM正适合放那些一成不变的程序,那么放在主板上的一块ROM芯片理所当然地承载着BIOS这段程序了。

  接着引出一个问题,访问ROM中BIOS是不是要先加载到内存里面在让CPU访问呢?不是的,其实ROM也是内存的一部分,是可以被CPU直接访问的。因此,我们插在主板上的内存条并不是我们眼里的所有内存,内存除了刚才所说的ROM和内存条外还包括外设,如显存,硬盘控制器等等,这些都是能够被CPU直接访问的。比如CPU访问0xC8000-0xEFFFF这块地址,并不是访问我们的内存条,而是直接访问到我们的硬件适配器的ROM或者内存映射的I/O。 下图解释了CPU访问内存各部分的流程:

   解释完上面两个问题,我们正式进入主题。

BIOS

   BIOS,Base Input & Output System,基本输入输出系统,它的主要工作是初始化硬件,硬件里有初始化的功能供BIOS调用;除此之外,BIOS还建立了中断向量表,建立之后,我们就可以通过int 中断号来对硬件进行IO操作,当然,BIOS也仅仅是初始化重要的硬件IO操作来保证计算机的运行,并不会面面俱到,在保护模式会对更多硬件做支持,但在实模式下保证基本的就够了,所以才说是“基本”输入输出系统。

加载BIOS的流程

  开机时,BIOS不可能自己加载自己,必然时被硬件所加载的。上面说到存储BIOS的ROM是块内存,是可以被CPU直接访问的。这ROM被映射到了地址0Xf0000~0xfffff处,入口地址,即程序开始执行的地址是0xffff0。所以我们只需将CPU的cs:ip值设置到0xffff0即可,cs:ip组合成0xffff0的方式有很多,不过根据过去的做法,是将CPU的cs:ip寄存器强制初始化为0xf000:0xfff0。这样,在开机的瞬间,CPU就会执行0xffff0下的代码。

BIOS----->MBR

  BIOS检测外设信息和初始化硬件,建立中断向量表后,BIOS就已经完成它的任务,最后就是将任务交给MBR了。BIOS最后会校验启动盘中的0盘0道1扇区,如过该上去末尾的两个字节是0x55和0xaa即说明这个扇区存储着MBR程序,于是就加载该扇区到物理地址0x7c00,执行jmp 0 :0x7c00跳到该扇区,继续执行MBR程序。

编写MBR

  在写代码之前,先简单介绍几个nasm标号: $代表当前行地址。$$代表当前section的地址。section可以命名某段代码。如果section有vstart修饰,$和$$都是基于vstart顺延的值。这样说的有点简单粗暴,下面看个例子。

  某个文件代码如下:

 section data vstart=0x500
    jmp $  
    jmp   $$     

  Jmp $中的$实际就是0x500

  Jmp $$中的$$实际就是0x500

  如果想获得文件中真实的地址,就用section.节名.start,如section.data.start = 0

  接下来直接给出MBR代码:

;主引导程序 
;------------------------------------------------------------
SECTION MBR vstart=0x7c00         
   mov ax,cs      
   mov ds,ax
   mov es,ax
   mov ss,ax
   mov fs,ax
   mov sp,0x7c00

; 清屏 利用0x06号功能,上卷全部行,则可清屏。
; -----------------------------------------------------------
;INT 0x10   功能号:0x06       功能描述:上卷窗口
;------------------------------------------------------
;输入:
;AH 功能号= 0x06
;AL = 上卷的行数(如果为0,表示全部)
;BH = 上卷行属性
;(CL,CH) = 窗口左上角的(X,Y)位置
;(DL,DH) = 窗口右下角的(X,Y)位置
;无返回值:
   mov     ax, 0x600
   mov     bx, 0x700
   mov     cx, 0           ; 左上角: (0, 0)
   mov     dx, 0x184f       ; 右下角: (80,25),
               ; VGA文本模式中,一行只能容纳80个字符,共25行。
               ; 下标从0开始,所以0x18=24,0x4f=79
   int     0x10            ; int 0x10

;;;;;;;;;    下面这三行代码是获取光标位置    ;;;;;;;;;
;.get_cursor获取当前光标位置,在光标位置处打印字符.
   mov ah, 3        ; 输入: 3号子功能是获取光标位置,需要存入ah寄存器
   mov bh, 0        ; bh寄存器存储的是待获取光标的页号

   int 0x10        ; 输出: ch=光标开始行,cl=光标结束行
            ; dh=光标所在行号,dl=光标所在列号

;;;;;;;;;    获取光标位置结束    ;;;;;;;;;;;;;;;;

;;;;;;;;;     打印字符串    ;;;;;;;;;;;
   ;还是用10h中断,不过这次是调用13号子功能打印字符串
   mov ax, message 
   mov bp, ax        ; es:bp 为串首地址, es此时同cs一致,
            ; 开头时已经为sreg初始化

   ; 光标位置要用到dx寄存器中内容,cx中的光标位置可忽略
   mov cx, 5        ; cx 为串长度,不包括结束符0的字符个数
   mov ax, 0x1301    ; 子功能号13是显示字符及属性,要存入ah寄存器,
            ; al设置写字符方式 ah=01: 显示字符串,光标跟随移动
   mov bx, 0x2        ; bh存储要显示的页号,此处是第0页,
            ; bl中是字符属性, 属性黑底绿字(bl = 02h)
   int 0x10        ; 执行BIOS 0x10 号中断
;;;;;;;;;      打字字符串结束     ;;;;;;;;;;;;;;;

   jmp $        ; 使程序悬停在此

   message db "1 MBR"
   times 510-($-$$) db 0
   db 0x55,0xaa

  这个MBR的功能就是在屏幕上打印字符串”1 MBR”,背景色为黑色,前景色为绿色。根据注释看看代码意思,不需要太深入理解,后面还会讲到。

  将代码文件命名为mbr.S,进入该文件所在位置,输入下面的指令:

nasm -o mbr.bin mbr.S

  就会看到当前目录生成了mbr.bin:

  然后我们需要将该可执行文件放入启动盘,具体来说是0盘0道1扇区中。

  输入下列指令,将mbr.bin写入hd60M.img,hd60M.img是上一节安装bochs那里生成的。

 dd if=/your_path/mbr.bin of= /your_path /hd60M.img bs=512 count=1 conv=notrunc

  

  进入bochs安装目录,输入bin/bochs -f bochsrc.disk:

bin/bochs -f bochsrc.disk

  

  

  回车后,输入c继续运行,显示结果如下图:

  至此简单的MBR功能就完成了。

猜你喜欢

转载自www.cnblogs.com/thougr/p/11349531.html