#Orange's 自制OS系列笔记(4)#引导扇区的实现


引导扇区(boot sector) 是设备的第一个扇区,大小为512个字节,并且以0xAA55结束。当机器加电启动后,如果选择从软盘启动,会检查软盘的0面0磁道1扇区,如果它以 0xAA55结束,就认为它是一个引导扇区,之后把引导扇区内容加载到用户指定的内存地址处,并从此处开始执行,从此,计算机的控制权从处理器转移给了操作系统。

先来看引导扇区的代码:
        org	07c00h			
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	call DispStr			
	jmp	$			
DispStr:
	mov	ax, BootMessage
	mov	bp, ax			
	mov	cx, 38			
	mov	ax, 01301h		
	mov	bx, 000ch		
	mov	dl, 0
	int	10h			
	ret
BootMessage:            db	"Hey! I am Rooeye, welcome to my blog!"
times 	510-($-$$)	db	0	
dw 	0xaa55 

这段代码实现的功能就是在开机的时候让屏幕显示   "Hey! I am Rooeye, welcome to my blog!"



实际上我们实现的就是在屏幕上显示一个特定字符串,并且该字符串背景色是黑色,前景色是红色,并且高亮,显示位置是在第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	   ;字符串长度为 38
mov	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, cs
mov	ds, ax
mov	es, ax

bp存储的就是字符串的偏移地址

mov	ax, BootMessage
mov	bp, ax 

因为boot sector的大小为512个字节,所以先填充了 510-($-$$) 个字节,然后在定义了一个字类型数据 0xaa55.

times 	510-($-$$)	db	0	;$表示当前行被汇编后的地址 
                                        ;$$表示section开始处被汇编后的地址
dw 	0xaa55		                ;引导扇区的结束标志为 0xaa55

上面提到,cx寄存器是用来存储字符串的长度的,如果我们想显示任意字符串,那么cx的值就必须改变,有没有什么方法可以直接计算出字符串长度而不用我们人为指定呢?当然是有的啦,看如下代码:

        org	07c00h			
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	call	DispStr			
	jmp	$			
DispStr:
	mov	ax, BootMessage
	mov	bp, ax			
	mov	cx, strlen		
	mov	ax, 01301h		
	mov	bx, 000ch		
	mov	dl, 0
	int	10h			
	ret
BootMessage:		db	"Nowcoder is so good!"
strlen 			equ      $-BootMessage
times 	510-($-$$)	db	0	
dw 	0xaa55				

代码的第17行使用  $ - BootMessage 计算字符串长度,在第10行把strlen传递给通用寄存器CX即可。下面分别是使用bochs虚拟机和Vmare的运行上述代码的结果,运行后会输出  "Nowcoder is so good!"





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

该系列笔记也载于俺的个人网站:      http://zhangjinkun.com        , 欢迎访问。

全部评论

相关推荐

三年之期已到我的offer快到碗里来:9硕都比不上9本
点赞 评论 收藏
分享
Natrium_:这时间我以为飞机票
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务