引言: 这是我的一小步,却是…好吧还是一小步
计算机的启动过程
第一个运行的软件是BIOS(base input and output system),所以了解计算机的启动过程,我们就得了解BIOS。
BIOS存在只读存储器ROM里面, 在8086(一个cpu)的实模式内存布局下,BIOS有1MB的大小空间,也就是(2^20bits),BIOS的载入地址为0xF0000~0xFFFFF。这64KB
是ROM,里面存了BIOS的代码, BIOS建立了中断向量表, 所以我们可以用 “int 中断号” 来实现硬件调用.
0-0x9FFFF是DRAM (动态随机访问内存), 640kb的大小, 对于8086来说,这就是主存部分, 我们可以发现主存总是比地址总线要小, 因为地址总线还要分配地址给各种外部设备,不只是内存.
由于CPU访问内存是分段访问。所以具体为段基址往左边偏移四位(十六进制下就是一位)加上偏移地址,所以在开机的一瞬间,cs:ip 寄存器会被强制初始化为 0xF000:0xFFF0,我们来简单换算一下, F0000 + FFF0 -> FFFF0, 也就是说,BIOS的入口地址是0xFFFF0。而我们前面说BIOS的终止地址是 0xFFFFF. 这么小的一个空间(只有16个字节)什么也干不了,所以必然是一个跳转指令. (jmp f000:e506), 这条指令的意思是,从地址 0xFE506 开始执行。接下来,BIOS在这个位置不断的检测各种内存,显卡等设备有没有问题, 然后在 0x000 - 0x3FF 建立数据结构中断向量表.
执行到这,BIOS完成了自己的使命. 接下来就轮到磁盘了, BIOS认为如果磁盘扇区最后连个字节分别是0x55 和 0xaa 就认为存在程序,会继续执行, 把第一个可用的磁盘加载到 0x7c00 (魔数). MBR的大小要为512字节, 占第一个扇区让上面两个magic number 正好出现在最后两个字节.
简单讲一下 bin 格式和 ELF 格式的区别, bin格式是纯二进制文件, 在可执行文件中什么样子,在内存中就什么样子, 而ELF还包含了,程序的内存布局和位置等信息.
接下来是一个简单的MBR调用BIOS中断来显示输出的程序. 这个程序的作用是显示一个字符串 “Hello, world!” 到屏幕上. 将这个程序编译完后,如何放到第一个扇区作为mbr运行呢. 用dd这个命令 就可以把编译后的程序放到第一个扇区了.dd if=/home/meiran/Documents/OS/mbr.bin of=./hd60M.img bs=512 count=1 conv=notrunc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| ;主引导程序 ;----------------------------------------------------- SECTION MBR vstart=0x7c00 ;告诉编译器,起始地址是0x7c00 mov ax,cs ;因为BIOS执行完毕后cs:ip为0x0:0x7c00,所以用cs初始化各寄存器(此时cs=0) mov ds,ax ;ds、es、ss、fs不能给立即数初始化,需要用ax寄存器初始化 mov es,ax mov ss,ax mov fs,ax mov sp,0x7c00 ;初始化堆栈指针,因为目前0x7c00以下的内存暂时可用
;清屏利用0x06号功能,上卷全部行,则可清屏 ;----------------------------------------------------- ;INT 0x10 功能号:0x06 功能描述:上卷窗口 ;----------------------------------------------------- ;输入: ;AH 功能号= 0x06 ;AL = 上卷行数(如果为0,表示全部) ;BH = 上卷行属性 ;(CL,CH) = 窗口左上角的(X,Y)位置 ;(DL,DH) = 窗口右下角的(X,Y)位置 ;无返回值:
mov ax,0x600 ;上卷行数:全部 功能号:06 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,11 ;cx为串长度,不包括结束符0的字符个数 mov ax,0x1301 ;子功能号13时显示字符及属性,要存入ah寄存器, ;al设置写字符串方式al=01:显示字符串,光标跟随移动 mov bx,0x2 ;bh存储要显示的页号,此处时第0页, ;bl中式字符属性,属性黑底绿字(bl = 02h) int 0x10 ;执行BIOS 0x10 号中断 ;;;;;;;; 打字字符串结束 ;;;;;;;; jmp $ ;使用程序悬停在此
message db "hello world" times 510-($-$$) db 0 db 0x55,0xaa
|
运行结果如下图