打造操作系统(1.2)引导扇区中的疑问

上回(1.1)我们成功的制作出了一个可引导计算机启动的引导扇区,不过,对于初学者而言,可能这个代码看起来有点困难,不过没关系,笔者在这里一一解释。

首先是引导扇区的汇编代码,如下:

	org	0x7c00

	jmp entry
	db	0x90

entry:
	mov ax,0			;设置段寄存器
	mov ss,ax
	mov ds,ax
	mov es,ax
	mov sp,0x7c00		;设置栈顶指针

	mov si,msg			;要打印的字符串所在地址
putloop:
	mov al,[si]			;取出一个字符
	add	si,1			;字符串指针后移
	cmp al,0			;取出的字符是否为'\0'
	je	halt			;如果是,则挂起系统
	;不是则打印字符
	mov ah,0x0e			;黑底白字
	mov bx,15
	int 0x10			;调用已有的中断程序打印字符
	jmp	putloop			;继续打印
	
halt:
	hlt
	jmp	halt

msg:
	db	"hello os world!"
	db	0
	
times	510-($-$$)	db	0
	db	0x55,0xaa
笔者在这里简单解释一下。一开始 org 0x7c00这个并非汇编指令,而是一个“伪指令”,什么意思呢?这个代码是告诉编译器,我们的程序将会被加载到0x7c00处执行。为什么要这样写呢?如果一个程序没有加载到正确的位置,它在执行过程中是会遇到很多问题的,比如全局变量不能正确访问。那你可能就有疑问了,我平时写的程序,它们的加载又是怎么回事呢?如果两个程序的正确加载位置冲突了怎么办?它们就不能同时运行了?这个,按我们上面的说法是不可以的,不过,由于我们平时写的程序是在一个比较完善的操作系统下运行的,这些问题将会由操作系统区考虑,而我们今后也会遇到这样的问题。

接下来的代码,笔者不是很愿意去一一解释。不明白的读者可以去看看 8086实模式下的汇编语言程序设计 相关的书籍,如王爽的《汇编语言》。我相信你能够完全明白。如有困难,可在评论中提出。

在我们使用了nasm编译了这个boot.asm文件之后,我们不妨再将其反汇编,nasm自带了一个反汇编工具ndisasm,打开nasm的命令行,切换到boot.bin(编译出来的引导扇区文件)所在目录,输入ndisasm boot.bin就可以看到反汇编后的结果了(这里会把一些数据也当做代码反汇编了,如字符串hello,读者需要根据实际情况进行区别)。以下是部分代码:

00000000  EB01              jmp short 0x3
00000002  90                nop
00000003  B80000            mov ax,0x0
00000006  8ED0              mov ss,ax
00000008  8ED8              mov ds,ax
0000000A  8EC0              mov es,ax
0000000C  BC007C            mov sp,0x7c00
0000000F  BE277C            mov si,0x7c27
00000012  8A04              mov al,[si]
00000014  83C601            add si,byte +0x1
00000017  3C00              cmp al,0x0
00000019  7409              jz 0x24
0000001B  B40E              mov ah,0xe
0000001D  BB0F00            mov bx,0xf
00000020  CD10              int 0x10
00000022  EBEE              jmp short 0x12
00000024  F4                hlt
00000025  EBFD              jmp short 0x24
从汇编指令中我们可以看到,原来的代码 mov si,msg(13行)反汇编之后成了这样



也就是说msg变成了0x7c27(这也就是msg所表示的字符串所在的地址),你是不是觉得眼熟了?对,看起来像是0x7c00加上了某个数,对,加上的那个数就是hello这个字符串在boot.bin中的文件偏移。当我们的boot.bin被加载到了0x7c00处时,hello这个字符串自然就在0x7c27这个位置去了。如果你在前面写的是org 0x100的话,那么这时候mov si,msg就会被编译为mov si,0x127,那么,要使程序正常执行,程序就必须被加载到内存0x100处,后面的地址0x127才是hello那个字符串所在的真实地址。

这里,我们也看出来了一些问题。程序的加载地址和代码是密切相关的,一个程序一旦编译之后,通常来说它只能被加载到一个固定的位置才能够保证程序的正常运行。如果不能保证加载的位置正确,那么程序的执行结果将是不可预知的。

接下来,我们来看后面的一些代码。

程序中有一个putloop。笔者读过一些编写操作系统的书,有的使用的是一个直接打印字符串的中断程序(该中断需要指定字符串长度),而这里笔者的选择有所不同,笔者选择了一个打印字符的中断程序,将字符串一个一个打印出来,直至遇到'\0'。这样的好处在于,程序的通用性变得更好,可以打印任意的以'\0'结尾的字符串,而无需每次计算字符串长度。只要将要打印的字符串的地址赋值给si寄存器,然后调用即可打印出字符串。不过这个代码还不能直接这样使用,因为这个putloop把字符串打印完之后就跳转到了挂起系统的代码处了,而且也没有ret指令(相当于C中的return,函数返回)。所以它仅仅是一个loop而不是function。又兴起的读者可以将它稍加修改,变成一个能够随意使用的函数吧,你也能学到更多的东西,有更多的经验。

(此处红色为后面补充)

还有便是在代码末尾处的很奇怪的代码:

times	510-($-$$)	db	0
不过你是否想过,为什么我们编译之后的文件刚好是512字节大小呢?为什么这里是510呢?笔者不太想直接解释。因为最后还有两个字节是0x55,0xAA已经确定了,所以出现了510,而对于($-$$),$是当前代码在编译后相对源码开始的地址,而$$是指当前段开始的地址,510-($-$$)计算的结果便是,要使文件达到510字节后面还需要填充的字节数,加上最后两个引导扇区独有的字节,便是512字节了。

谢谢。如果疑惑,可在评论中提出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值