对应书中第2.3节:让mbr飞一会 第58页
在电脑未开机前,BIOS就被事先写入到内存的F0000~FFFFF中。此区域为ROM(非易失性存储器,断电仍可以保存数据。)
MBR被加载到磁盘的0盘0道1扇区(CHS方式)
CHS 方式中扇区的编号是从1开始的
当电脑按下power键后,cs:ip会被强制置为F000:FFF0 ,所以cpu会执行内存地址FFFF0处的指令。
FFFF0处的指令为跳转指令jmp far f000:e05b。(也就是BIOS区域中某个地址)
跳转后,BIOS便马不停蹄地检测内存、显卡等外设信息,当检测通过,并初始化好硬件后,开始在内存中 0x000~0x3FF处建立数据结构,中断向量表 IVT 并填写中断例程。
BIOS 最后一项工作校验启动盘中位于0盘0道1扇区的内容。
如果此扇区末尾的两个字节分别是魔数0x55和0xaa,就会把该扇区内容加载到内存地址 0x7c00处,
然后执行跳转指令jmp 0: 0x7c00
编写mbr.s程序,该程序可以在显示屏打印字段。将该程序加载到磁盘的0盘0道1扇区,使用bochs模拟,确保当计算机执行完BIOS后可以执行mbr.s,在显示屏打印字段。
; mbr.S
; 主引导程序
; --------------------------------------------------
SECTION MBR vstart=0x7c00 ; 把起始地址编译为 0x7c00
mov ax, cs ; cs 代码段寄存器
mov ds, ax ; dx 数据段寄存器
mov es, ax ; es 附加段寄存器
mov ss, ax ; ss 堆栈段寄存器
mov fs, ax ; fs 80386 后添加的寄存器,无全称
mov sp, 0x7c00 ; sp 堆栈指针寄存器
; 清屏
; --------------------------------------------------
; 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
mov dx, 0x184f ; 右下角: (80, 25)
; VGA 文本模式种,一行只能容纳 80 个字符,共 25 行
; 下标从 0 开始,所以 0x18=24, 0x4f=79
int 0x10 ; int 0x10
;;;;;;;;;;;;;下面这三行代码获取光标位置;;;;;;;;;;;;;;;;
; .get_cursor 获取当前光标位置,在光标处打印字符
mov ah, 3 ; 3 号子功能
mov bh, 0 ; 待获取光标的页号
int 0x10 ; 输出:
; ch = 光标开始行,cl = 光标结束行
; dh = 光标所在行号,dl = 光标所在列号
; 打印字符串
mov ax, message
mov bp, ax ; es:bp 为串首地址
mov cx, 5 ; cx 为串长度,不包括结束符 '\0'
mov ax, 0x1301 ; 13 号子功能
; ah = 13
; al = 01: 写字符方式,显式字符串,光标跟随移动
mov bx, 0x2 ; bh = 0,要显示的页号
; bl = 02,字符属性,黑底绿字
int 0x10
;;;;;;;;;;;;;;;;;;;;;;打印字符串结束;;;;;;;;;;;;;;;;
jmp $; 程序悬停在此
message db "1 MBR"
times 510-($-$$) db 0
db 0x55,0xaa
这次代码为实模式下对x86架构的汇编编程,实模式下段寄存器,通用寄存器如下:
本代码使用了BIOS提供好的中断服务程序完成了在显存0页光标处打印字符串。
每个外设,如显卡、键盘、各种控制器等,都有自己的内存(RAM+ROM),外设将自己的中断例程以及初始化例程都事先写好存储在它们的ROM中。
根据规范,该ROM的第一个存储单元内容是0x55,第二个是0xAA,第三个是该ROM以512字节为单位的代码长度,第四个开始是代码。
从1MB下的内存0xA0000到0xFFFFF这部分内存中,一部分是专门用来给外设ROM作内存映射的。
BIOS会在运行期间扫描0xC0000到0xE0000之间内容,发现某个区域前两字节是0x55和0xAA时,说明该区域有代码,在做累加和与第三个字节比较,无误则说明代码无误,就从第四个字节进入,执行外设自带的初始化例程,填写,BIOS中断向量表IVT(实模式1MB下0x000到0x400),使他们指向BIOS提供中断例程
先调用BIOS中断的0x10号中断的0x06子功能上卷80行清屏,
在调用0x10号中断的0x03号子功能获取显存第0页的光标位置,输出到dx
最后调用13号子功能在显存第0页光标处打印字符串,且光标跟随移动。
1.使用vstart=0x7c00,则message表示的地址还要加上0x7c00才是编译后实际送入寄存器的地址。$ $$这类符号均是如此。
2.用cs、ax将段寄存器清0
3.用寄存器bp去访存获得长度为5的字符串时,实模式下访存方式为es:bp,只不过es已被ax赋值为0,所以访存的仍然是message+0x7c00(即"1 MBR"的物理地址)。
./bximage -mode=create -imgmode=flat -hd=60 -q Seven.img
注:此命令和书上不同,因为环境版本不同 -size会报错。
输入命令
./bximage --help
nasm -o mbr.bin mbr.s
将mbr.s编译成mbr.min。
dd if=/你的mbr.bin的路径/mbr.bin of=/你的自定义硬盘的路径/自定义硬盘名称 bs=512 count=1 seek=0 conv=notrunc
比如我自己的
dd if=/home/Seven/bochs2.68/bin/mbr.bin of=/home/Seven/bochs2.68/bin/Seven.img bs=512 count=1 seek=0 conv=notrunc
输入命令
./bochs -f bochsrc.disk
然后输入c继续。