前阵子找到本书<x86汇编语言 从实模式到保护模式>,前半本是关于实模式下编程,后半本是保护模式下编程,正好最近在看linux中断相关的内容,索性就买了回来翻翻。
其实,于渊大神诺干年前也写过类似的书<自己动手写操作系统>(现在再版叫 orange操作系统的实现),何奈,当时惰性较大,书买来后一直是压箱底状态,这回连这本书也一起找了出来,相互对照着看。
<x86>书中用的虚拟磁盘是vhd类型,<自>书中用的是img类型。因为<x86>书中直接提供了vhd磁盘以及烧写vhd磁盘的工具,同时给出了配置bochs的步骤,所以,如果我坚持的一步步做下来,也会使用这种类型的虚拟磁盘。作者虽然没有随书提供光盘,但是在csdn上提供了免积分下载的源码和工具。
我的virtualbox在win7上不能加载vhd文件,只能直接用bochs观察结果,虽然没什么区别,但还是觉得有点可惜。
进入正文,虽然作者提供了源码,但是直接抄下来还发为原创有点说不过去,只能自己写了,所以标题是仿照着写bootloader。
先贴代码,预期是在屏幕左上角输出"hello world"
section code align=16 vstart=0x7c00 jmp entry string db 'hello world',0 length db 0 entry: ;程序从0x7c00开始执行 mov ax,0xb800 mov es,ax mov ax,cs mov ds,ax ; xor cx,cx mov cx,length-string xor si,si xor di,di mov bx,string lab1: mov ax,[ds:bx+di] mov [es:si],ax mov byte [es:si+1],0x07 inc di add si,2 loop lab1 jmp $ times 510-($-$$) db 0 db 0x55,0xaa
程序刚启动时CS:IP停留在逻辑地址f0000:fff0(物理地址ffff0,整个能访问的内存才到0xfffff,好在这个地方安排了一个跳转指令跳到f000:e05b,要不然都不够用的)。从f0000-fffff是bios的空间,程序跳到fe05b处执行一些初始化,这个就不管了直接在0x7c00处下断点,毕竟这是bootloader的入口点
恢复运行后,程序在0x7c00处停下。其实,前面下断点时,我就想看下0x7c00处的内存是不是被改为0xcc不过啥都没看到,看来int3还是软件处理的异常。。。
看下0x7c00处的内存情况
lst文件符合阅读顺序,从左往右字节以此增加;bochs xp查看双字内存,每个双字最右边是低字节,最左边是高字节。因此,bochs输出的0x000de9对应lst文件最开始的E90D00(jmp entry指令);随后的0x68 0x6f6c6c65对应了lst文件+0x03开始的68656c6c,对照ascII,是"hell"
hello world最终要输出到bochs的屏幕上,8086可访问的1M内存中,B8000-BFFFF对应屏幕显示区,看下指令刚执行到0x7c00时,这块区域的值是什么
b8000开始的内存以此为50b06c0b650b780b,按<x86>P50页上描述,“屏幕上的每个字符对应显存中的两个连续的字节,前一个是ascii字节,后一个是字符显示属性”,来看一下,到底显存里存了什么ascii码:506c6578对照ascii表以此是Plex。好吧,看下现在屏幕上显示的内容:
前4个字母的确是Plex。
最后,让程序全速,最终输出:
左上角已经看到hello world输出了。
好吧,先写到这,下线后再调试一会,总结后面再补上。有了这个开头,真希望自己能继续下去。
补上一些总结:
1)有效的主引导扇区,总大小为512B,最后两字节为0x55,0xaa。系统加点引导后,bios将该扇区加载到逻辑地址0x0000:0x7c00处,然后jmp 0x0000:0x7c00继续运行;(<x86>P45)
2) 8086可以访问1M内存。0x00000-0x9FFFF属于常规内存,由内存条提供;0xF0000-0xFFFFF由ROM-BIOS提供(刚复位时,CS:IP指向0xFFFF0);从0xA0000-0xEFFFF由外部设备提供,其中显存占据了0xB8000-0xBFFFF这段物理区域。(<x86>P48)。程序开始时,
mov ax,0xb800 mov es,ax使es指向0xb800段
3) 在上面代码中有一句:mov bx,string 经过汇编,在lst文件中显示的内容为:BB[0300],机器指令中哪来[]这种东西,感觉有点像重定位,于是加了几句无关的代码:
mov ax,string
lea ax,[string]
mov ax,entry
编译后在lst文件中对应的内容为:
00000010 B8[0300] mov ax,string
00000013 8D06[0300] lea ax,[string]
00000017 B8[1A00] mov ax,entry
把这个新生成的bin文件烧入,启动bochs再次调试,并在入口代码处反汇编:
距离0x7c00偏移0x10 0x13 0x17处的几条指令取的偏移值已经和lst文件中显示的不同了,因此可以确定确实经过了一次重定位,把指令中引用汇编地址的地方全部修改为加载地址