《自己动手写操作系统》书中第二页的代码
1-1
中对于
org 07c 00h
是这样注释的:
“
告诉编译器程序加载到
7c
00h
处
”
org 0 7c 00h
mov ax,cs
mov ds,ax
mov es,ax
call screen
jmp $
screen:mov ax,bootmsg
mov bp,ax
mov cx,16
mov ax,01301h
mov bx,000ch
mov dl,0
int 10h
ret
bootmsg:db'Hello OS world!'
times 510-($-$$) db 0
dw 0xaa55
但是我看到一些资料说,
bios
会自动将引导程序加载到
07c
00h
处,也就是说无论代码是什么都会被
bios
加载到
07c
00h
处,那么为什么还要写呢?后来我去掉
org 7c 00h
试了一下,仍可以启动。只是显示乱码。
org
会在编译期影响到内存寻址指令的编译(编译器会把所有程序用到的段内偏移地址自动加上org 后跟的数值),而其自身并不会被编译成机器码。就是为程序中所有的引用地址(需要计算的相对地址)增加一个段内偏移值。org 指令本身并不能决定程序将要加载到内存的什么位置,它只是告诉编译器,我的程序在编译好后需要加载到xxx 地址,所以请你在编译时帮我调整好数据访问时的地址。
如果没有
org
指令,那么编译器计算偏移量(虽然
nasm
中没有
offset
但编译器仍会进行这个运算)是从
0000
开始的,如果有了
org
,就会在最后加上
org
后面跟的数字。
在这个引导程序中:mov ax,bootmsg
在编译的时候实际上是将
mov ax,bootmsg
所在的地址与
bootmsg
同当前语句(
mov ax,bootmsg
)地址的偏移量相加所计算出来的。没有
org
则偏移量从
0000h
开始,于是虽然整个代码被加载到
0x 7c 00
处,但当执行到mov ax,bootmsg一句的时候,由于没有算入偏移量
7c
00
从而没有将
bootmsg
字符串的首地址放入
ax
,以至发生错误。
Ndisasm -o 0x0000 boot.bin >> disboot1.asm
所得到的汇编文件
:
Ndisasm �Co 0x 7c 00 boot.bin >> disboot2.asm
所得到的汇编文件
:
通过反汇编,模拟了引导程序被加载到
0000
:
0000
处和
0000
:
7c
00
处时的不同情形。第一次为0000:
00
00
第二次为
0000
: 7C 00。而对于
mov ax,bootmsg
一句的翻译却是一样的,都是
mov ax,0x 7c 1e
。显然第一次发生了错误(因为在整个程序中就没有出现0x 7c 1e这个地址,也就是说这是个无效地址)。错误产生的原因就是由于当代码被编译器编译的时候编译器是按照从
7c
00
处开始计算地址的。也证明了
bootmsg
的地址是由编译器计算出来的。
最后我们来分析一下为什么有时候去掉org 指令程序也能正常执行?
如:
ORG 7C 00H
msg: db 'HELLO WORLD',0
MOV DX, OFFSET msg
在有ORG 7C 00H的情况下,MOV DX, OFFSET msg对应的指令为MOV DX, 7C 4B(这里4B为msg在当前数据段中的偏移位置)如果没有ORG 7C 00H,那么真正被执行的指令将为MOV SI,004B,试想,BIOS已经将该代码装载到0000: 7C 00处,0000:0000�C0000: 7C 00之间的数据可能为其他更重要的数据,如果使用004B就得不到我们所要访问字符串msg,因为我们的字符串被BIOS放在 7C 4B这里了,所以我们的程序(最终由编译器来完成)就必须迎合BIOS的这种规定了。
因此,如果在程序中没有牵扯到地址的计算,那么完全可以不写
org
(没有做实验验证)
以下是我参考的文章,给了我很大的帮助。可以从我的blog资源中下载
http://blogimg.chinaunix.net/blog/upfile/070306125420.pdf