Orange’s 自制操作系统系列笔记(4)—一个引导扇区的具体实现
引导扇区(boot sector)是设备的第一个扇区,大小为512个字节,并且以0xAA55结束。当机器加电启动后,如果选择从软盘启动,会检查软盘的0面0磁道1扇区,如果它以0xAA55结束,就认为它是一个引导扇区,之后把引导扇区内容加载到用户指定的内存地址处,并从此处开始执行,从此,计算机的控制权从处理器转移给了操作系统。
先来看引导扇区的代码:
org 07c00hmov ax, csmov ds, axmov es, axcall DispStrjmp $DispStr:mov ax, BootMessagemov bp, axmov cx, 38mov ax, 01301hmov bx, 000chmov dl, 0int 10hretBootMessage: db "Hey! I am Rooeye, welcome to my blog!"times 510-($-$$) db 0dw 0xaa55
这段代码实现的功能就是在开机的时候让屏幕显示 “Hey! I am Rooeye, welcome to my blog!”
先来看其运行结果,用虚拟机VMare加载其生成的软盘映像文件:

实际上我们实现的就是在屏幕上显示一个特定字符串,并且该字符串背景色是黑色,前景色是红色,并且高亮,显示位置是在第0行。这段代码最重要的莫过于第14行的 int 10h ,int 10h中断例程是BIOS提供的中断例程,其中包含了多个和屏幕输出相关的子程序,接下来就了解下 int 10h。
通常一个中断例程都会包含多个内部子程序,执行哪一个子程序根据传递进来的参数而定,而BIOS中的中断例程,都是用8位通用寄存器AH来传递使用的子程序的编号。在本程序的第11行:
mov ax, 01301h
因为AX = 0x1301 , 则 AH = 0x13,即最终会执行第10h号中断的编号为13h的子程序,而该子程序的作用就是用来显示一个字符串,其具体调用参数见下:
ES:BP = 串地址CX = 串长度DH = 起始行DL = 起始列BH = 页号
AL和BL寄存器也会用来传递参数,但是比较复杂,下面详细解释。
AL寄存器一共有8位,但是只使用低两位,高6位并不使用:
如果AL=0,表示目标字符串仅仅包含字符,属性在BL中包含,不移动光标如果AL=1,表示目标字符串仅仅包含字符,属性在BL中包含,移动光标如果AL=2,表示目标字符串包含字符和属性,不移动光标如果AL=3,表示目标字符串包含字符和属性,移动光标
在该引导扇区代码中,因为AX = 0x1301,则 AL = 0x01。
BL寄存器主要是用来定义一些颜色属性格式:

若 BIT7 = 1 ,背景闪烁若 BIT3 = 1 ,前景色高亮显示BIT4~BIT6 表示背景色BIT0~BIT2 表示前景色
根据上面的知识可以对源代码得到如下分析:
mov cx, 38 ;字符串长度为 38mov ax, 01301h ;AH = 13h,AL=01h,在屏幕上打印字符串mov bx, 000ch ;页号为0,不闪烁,背景色为黑色,前景色高亮显示,前景色为红色mov dl, 0 ;在第0列开始显示
从而达到显示黑底红字的效果。
上面提到 AH = 13H的时候,调用参数ES:BP 传递字符串的地址。ES寄存器存储段地址,BP寄存器存储段内偏移地址,实际物理地址 = 段地址*16 + 偏移地址。
下面代码表示boot sector的内容会被加载到内存偏移地址为0x7c00处开始执行。
org 07c00h
因为段寄存器之间不能直接传递数据,所以先把段寄存器CS的内容传递给通用寄存器AX,然后再把其值送给段寄存器DS和ES。
mov ax, csmov ds, axmov es, ax
bp存储的就是字符串的偏移地址
mov ax, BootMessagemov bp, ax
因为boot sector的大小为512个字节,所以先填充了 510-($-$$) 个字节,然后在定义了一个字类型数据 0xaa55.
times 510-($-$$) db 0 ;$表示当前行被汇编后的地址;$$表示section开始处被汇编后的地址dw 0xaa55 ;引导扇区的结束标志为 0xaa55
上面提到,cx寄存器是用来存储字符串的长度的,如果我们想显示任意字符串,那么cx的值就必须改变,有没有什么方法可以直接计算出字符串长度而不用我们人为指定呢?当然是有的啦,看如下代码:
org 07c00hmov ax, csmov ds, axmov es, axcall DispStrjmp $DispStr:mov ax, BootMessagemov bp, axmov cx, strlenmov ax, 01301hmov bx, 000chmov dl, 0int 10hretBootMessage: db "Nowcoder is so good!"strlen equ $-BootMessagetimes 510-($-$$) db 0dw 0xaa55
代码的第17行使用 $–BootMessage 计算字符串长度,在第10行直接把strlen传递给通用寄存器CX即可。下面分别是使用bochs虚拟机和Vmare的运行上述代码的结果,运行后会输出 “Nowcoder is so good!”。


下一篇笔记写保护模式(Protect Mode)。