Linux内核系列—操作系统开发之HelloWorld,linuxhelloworld
org 07c00h ;伪指令,告诉编译器程序会被加载到7c00处 mov ax, cs mov ds, ax mov es, ax call DispStr ;调用显示字符串例程 jmp $ ;无限循环 DispStr: mov ax, BootMessage mov bp, ax ;ES:BP=字符串地址 mov cx, 16 ;CX=字符串长度 mov ax, 01301h ;AH=13,AL=01h mov bx, 000ch ;页号为0(BH=0)黑底红字(BL=0Ch,高亮) mov dl, 0 int 10h ;10h号中断 ret ;pop IP BootMessage: db "Hello, OS world!" times 510-($-$$)db 0 ;填充剩下的空间,使生成的二进制代码恰好为512字节 dw 0xaa55 ;结束标志
以上是个非常简单的操作系统helloworld源码,用bochs加载后如下图(bochs是个虚拟机,模拟操作系统加载),显示了红色的Hello,OS world,实际上是系统加载起来的样子:
源码解析:
1.org 07c00h和dw 0xaa55和times 510-($-$$) db 0
当计算机电源打开,会先进行加电自检(POST),然后寻找启动盘,如果是选择从软盘启动,计算机会检查软盘的0面0磁道1扇区,如果发现它以0xaa55结束,则BIOS认为它是一个引导扇区。一个正确的引导扇区除了以0xaa55结束之外,还应该包含一段512字节的执行码。一旦BIOS发现了引导扇区,就会将这512字节的内容装载到内存地址0000:7c00处,然后跳转到0000:7c00处将控制权彻底交给这段引导代码。
org 07c00h就是告诉编译器,这段代码是要被加载到这个地址的。所以BootMessage的偏移地址是相对于07c00h处开始的。BootMessage实际上也是一个地址,不是相对量
2.mov ax, cs、mov ds, ax、mov es, ax
让ds和es指向cs的段,也就是07c00h。因为程序会被加载到07c00h处,所以执行到这一步的时候CS指向07c00h
3.call DispStr
调用显示字符串的函数,在NASM中,任何不被[]括起来的标签或变量名都被认为是地址,如果是访问标签中的内容则必须使用[]。所以mov ax, BootMessage会把"Hello,OS world!"这个字符串的首地址传给寄存器ax。然后mov bp, ax赋给bp,如果没写org指令,则BootMessage编译的时候就是相对于本程序加载到地址0的偏移地址,但是这里是相对于07c00h为基址的偏移地址,所以这里显示了org的作用。
4.int 10h
10H中断是由BIOS对显示器和屏幕所提供的服务程序。使用int 10h服务程序时,必须先指定ah寄存器。
功能13h:
功能描述:在Teletype模式下显示字符串
入口参数:AH=13H
BH=页码
BL=属性(若AL=00H或 01H)
CX=显示字符串长度
(DH、DL)=坐标(行、列)
ES:BP=显示字符串的地址
AL=显示输出方式
0—— 字符串中只含显示字符,其显示属性在BL中。显示后,光标位置不变
1——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置改变
2 ——字符串中含显示字符和显示属性。显示后,光标位置不变
3——字符串中含显示字符和显示属性。显示后,光标位置改变
出口参数:无
5.$和$$
$-$$表示本行距离程序开始处的相对距离。times 510-($-$$) db 0表示将0这个字节重复510-($-$$)遍,也就是在剩下的空间中不停地填充0,直到程序有510字节为止。这样,加上结束标志0xaa55占用的2字节,恰好是512字节。