实模式寻址
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
