«操作系统真像还原» 第二章 编写MBR主引导记录

实模式寻址

8086 开机后,CS(代码段寄存器)和 IP(指令指针寄存器)的初始值不是由软件设置的,而是由 CPU 的硬件电路强制指定的,固定为CS=0xF000,IP=0xFFF0

0xF000 * 16 +0xFFF0 = 0xFFFF0

8086CPU的寄存器是16位的(最多只能存储0x0000~0xFFFF的数值),但它的地址线是20位的(20位地址线,能访问的物理内存范围是0x00000~0xFFFFF,共1MB,这也是实模式的最大内存寻址能力

为了解决16位寄存器寻址20位物理地址的矛盾,8086设计了段式寻址规则(仅实模式有效,保护模式会用更复杂的寻址方式):物理内存地址 = 段地址 × 16 + 偏移地址

MBR主引导记录

0xFFFF0处存放着一条跳转指令,用于跳转到BIOS程序,BIOS程序会对0盘0道1扇区的最后两个字节进行校验查看是否存放的是MBR主引导记录,如果是0x55,0xaa,就会将改扇区的512字节加载到内存0x7C00处

接下来我们会来写一个简单的MBR:

逻辑:

1.指定起始地址为0x7C00

2.调用bios中断进行清屏操作

3.调用bios中断获取光标所在位置

4.调用bios中断在光标位置出打印字符串

5.进入死循环然后将最后两个字节设置为0x55,0xaa,然后在其余地方补0以达到512字节大小

代码

复制代码
SECTION MBR vstart=0x7c00
    mov ax, cs
    mov ss, ax
    mov ax, 0x7c00
    mov sp, ax

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

                                ;;;;;;;;;;;;;;; 下面这三行代码获取光标位置 ;;;;;;;;;;;;;;;
                                ; INT 0x10 获取当前光标位置,在光标位置处打印字符串
                                ; 输入:
                                ; AH = 3 子功能号是获取光标位置,要存入ah寄存器
                                ; BH = 存储字符的页号(此处是第0页)
                                ; 输出:
                                ; CH=光标开始行,CL=光标结束行
                                ; DH=光标所在行,DL=光标所在列
    mov ah, 3
    mov bh, 0
    int 0x10
;;;;;;;;;;;;;;; 获取光标位置结束 ;;;;;;;;;;;;;;;;;;;;;;

                                ;;;;;;;;;;;;;;; 打印字符串 ;;;;;;;;;;;;;;;;;;;;;;;;;;;
                                ; 还是用10h中断,不过这次调用13号子功能打印字符串
                                ; 输入:
                                ; AH = 13 子功能号是显示字符及属性,要存入ah寄存器
                                ; AL = 设置显示字符方式,AL=01:显示字符串,光标跟随移动
                                ; BH = 存储要显示的页号,此处是第0页
                                ; BL = 字符属性,属性黑底绿字(bl= 02h)
                                ; CX = 字符串长度,不包括结束符0的字符个数
                                ; DH = 字符串要显示的行
                                ; DL = 字符串要显示的列
                                ; ES:BP = 字符串的段:偏移地址
    mov ax, message             ; es:bp 为串地址,此时同cs一致,
    mov bp, ax                  ; 开头时已经为es初始化
                                ; 光标位置要用dx寄存器中内容,cx中的光标位置可忽略
    mov cx, 5                   ; cx 为串长度,不包括结束符0的字符个数
    mov ax, 0x1301              ; ah设置显示字符方式=01:显示字符串,光标跟随移动
    mov bx, 0x02                ; bh存储要显示的页号,此处是第0页
                                ; bl 中是字符属性,属性黑底绿字(bl= 02h)
    int 0x10                    ; 执行 BIOS 0x10 号中断
;;;;;;;;;;;;;;; 打印字符串结束 ;;;;;;;;;;;;;;;;;;;;;;

jmp $                           ;使程序悬浮在此
message db "1 MBR"
times 510 - ($ - $$) db 0
db 0x55,0xaa

代码解析

SECTION MBR vstart=0x7c00 ;告诉编译器将起始地址编译为0x7c00


mov ax, cs ;cs刚开始时为0,此时将ax也设置为0

mov ss, ax ;将ss段寄存器设置为0

mov ax, 0x7c00 ;将ax设置为0x7c00来间接给段寄存器赋值

mov sp, ax ;将sp栈指针寄存器指向MBR起始地址


我们通过int 0x10,功能号为0x06 来调用上卷窗口的操作

mov ax, 0x0600 ;将AH功能号设置为0x06,AL=0x00

mov bx, 0x0700 ;BH存放上卷行属性,设置为0x07为黑底白字

mov cx, 0x0000 ;(CL,CH) = 窗口左上角的(X,Y)位置,此处为(0,0)

mov dx, 0x184f ;(DL,DH) = 窗口右下角的(X,Y)位置,0x18=24,0x4f=79,下标从0开始, 所以是第25行,一行80字符

int 0x10 ;调用中断


mov ah, 3 ;ah存放功能号

mov bh, 0 ;bh寄存器存储的是待获取光标的页号

int 0x10


mov ax, message ;message存放的是"1 MBR"字符串的偏移地址

mov bp, ax ;es:bp是字符串的首地址

mov cx, 5 ; cx 为字符串长度,不包括结束符0的字符个数

mov ax, 0x1301 ; ah子功能号0x13是显示字符及属性,al=0x01:显示字符串

mov bx, 0x02 ; bh存储要显示的页号,此处是第0页,bl中是字符属性,属性黑底 绿字(bl = 02h,07是黑底白字)


jmp ;使程序悬浮在此,是获取当前指令所在地址

message db "1 MBR" ;按字节存储"1 MBR"的ASCII码,message标签绑定

times 510 - ( - ) db 0 ;是当前指令地址,$$是程序开头地址,此处会将不足510字节的内容填 充0知道足够510字节

db 0x55,0xaa ;定义最后两个字节是0x55,0xaa

编译

nasm -o mbr mbr.s

用dd命令写入虚拟硬盘

dd if=/home/chipfesen/bochs/mbr of=/home/chipfesen/bochs/hd60M.img bs=512 count=1 conv=notrunc

if是要写的文件路径,of是被写入的文件路径,bs是块大小,此处是512字节,count是块数量,conv是如何转换文件,notrunc是不转换

启动

使用bin/bochs -f bochsrc.disk来启动

按下回车后输入c再回车

启动后会显示绿色的1 MBR

相关推荐
律宏阔5 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔5 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴5 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千5 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei375 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei375 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei375 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
GeW5 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
GeW5 小时前
工业场景中的Linux与数据库选型:高端化转型的技术底座
linux
yunwei375 小时前
eBPF 开发者教程: 简单的 XDP 负载均衡器
linux·后端·性能优化