《一个操作系统的实现》(一):不到20行的操作系统代码的解释

来源:互联网 发布:python实现svm smo 编辑:程序博客网 时间:2024/06/06 20:10

最开始的一段操作系统代码:

org 07c00h;告诉编译器程序加载到7c00处mov ax, csmov ds, axmov es, axcall DispStr;调用显示字符串例程jmp $;无限循环DispStr:mov ax, BootMessagemov bp, ax;ES:BP = 串地址mov cx, 16;CX = 串长度mov ax, 01301h;AH = 13, AL = 01hmov bx, 000ch;页号为0(BH=0)黑底红字(BL=0Ch,高亮)mov dl, 0int 10h;10h号中断retBootMessage:db "Hello, OS world!"times 510-($-$$)db 0;填充剩下的空间,使生成的二进制代码恰好为512字节dw 0xaa55;结束标志


org规定程序的起始地址

cs:代码段寄存器,一般用于存放代码;ds:数据段寄存器,一般用于存放数据;es:扩展段寄存器,使用时可以看作ds的扩展寄存器;(ss:栈段寄存器,一般作为栈使用)。段寄存器是为了对内存进行分段管理。

ax:通用寄存器。通用寄存器有AX,BX,CX,DX,BP,SP,SI,DI,均可做普通数据寄存器使用。此外,AX为累加器,CX为计数器,BX,BP为基址寄存器,SI,DI为变址寄存器,BP还可以是基指针,SP为堆栈指针。

2-4行的作用就是令ds和es两个段寄存器指向与cs相同的段,以便在以后进行数据操作的时候能定位到正确位置。段寄存器都指向了相同的段,但并不代表内容重叠,只是概念上的重叠。虽然放到一个段中,但是相互可以区分开。比如某一段既有数据也有代码,则代码在执行到数据之前,需要用户在编程时加上一个跳转指令以跳过此段中的代码。段寄存器一共有四个,为什么只将ds、es、cs指向同一段,ss呢?栈段寄存器比较特殊,不仅用户会用,系统也会自动使用,而且用户也许在不知道系统使用的情况下使用ss,这样就会导致冲突。避免这种冲突的方法是采用逆向的栈段。

在网上查了一下,X86有实模式和保护模式两种模式(书上第三章讲保护模式,学习这章时我再写篇以这两种模式为内容的文章),现在“Hello, OS world!”是实模式。

第五行所call的就是显示字符串的代码了,8-15行都是。jmp $ 代表无限循环,其中$代表当前行被汇编之后的地址。

8-9行将BootMessage的首地址赋给bp(前面说过啦 是基址寄存器,还可以是基指针)。对于DispStr中为什么要给ax、bx、dl赋值,BL=0Ch代表黑底红字等问题,参见此材料或此博文,这些在我看来应该算是int 10h的规定吧

下面说一下16-18行。db代表后面的数据以字节存放(dw代表以字存放,dd代表以双字存放)。 db即“分配一片连续的字节单元并初始化”。而times 510-($-$$)代表以0填充剩余空间。这里times可以理解为循环(PS:times、db、dw均属于伪指令——用于告诉汇编程序如何进行汇编的指令,它既不控制机器的操作也不被汇编成机器代码,只能为汇编程序所识别并指导汇编如何进行)。而$$表示一个节的开始处被汇编后的地址,这里程序只有1个节,所以$$实际上表示程序被编译后的开始地址。所以$-$$表示本行距离程序开始处的相对距离。times循环过后程序有510字节,为什么不是512字节呢?因为还要给结束标志0xaa55留两个字节。而dw基本含义与db相同,不同的是dw定义16位数据,高8位数据字节先存入低地址字节中,而低8位数据字节则再存入高地址字节中。


书上还说明了关于[]的使用问题,如下:

在NASM中,任何不被方括号[]括起来的标签或变量名都被认为是地址,访问标签中的内容必须使用[]。所以mov ax, BootMessage会把“Hello, OS world!”这个字符串的首地址传给寄存器ax。又比如,如果有foo dw 1,则mov ax, foo将把foo的地址传给ax,而mov bx, [foo]将把bx的值赋为1.实际上NASM中变量和标签一样,即foo dw 1等价于foo: dw 1,而且Offset这个关键字在NASM中也是不需要的,因为不加方括号时表示的就是Offset。总结为NASM的一大优点:要地址就不加方括号,也无需额外用什么Offset,想要访问地址中的内容就必须加上方括号,代码规则非常鲜明,一目了然。

原创粉丝点击