汇编语言源程序的编译符合一种假设,即编译后的代码将从某个内存段中,偏移地址为0 的地方开始加载。这样一来,如果有一个标号“label_a”,它在编译时计算的汇编地址是0x05,那么,当程序被加载到内存后,它在段内的偏移地址仍然是0x05,任何使用这个标号来访问内存的指令都不会产生问题。
但是,如果程序加载时,不是从段内偏移地址为0 的地方开始的,而是0x7c00,那么,label_a 的实际偏移地址就是0x7c05。这时,所有访问label_a 的指令仍然会访问偏移地址0x05,因为这是在编译时就决定了的。实际上,这样的问题在上一章就遇到过。在那里,因为我们已经知道程序将来的加载位置是0x0000:0x7c00,所以才有了这样古怪的写法:
不得不说,0x7c00 就是理论和现实之间的差距。
在主引导程序中,访问内存的指令很多,如果都要加上0x7c00 无疑是很麻烦的,这个我们已经看到了。其实,产生这个问题的根源,就是因为程序在加载时,没有从段内偏移地址为0 的地方开始。
好在Intel 处理器的分段策略还是很灵活的,逻辑地址0x0000:0x7c00 对应的物理地址是0x07c00,该地址又是段0x07C0 的起始地址。因此,这个物理地址其实还对应着另一个逻辑地址0x07c0:0000,如图6-1 所示。
看到了吧?我们可以把这512 字节的区域看成一个单独的段,段的基地址是0x07C0,段长512 字节。注意,该段的最大长度可以为64KB,但是在这里,我们实际上仅使用512 个字节。尽管BIOS 将主引导扇区加载到物理地址0x07c00 处,但我们却可以认为它是从0x07c0:0x0000 处开始加载的。
图6-1 以两个逻辑段的视角看待同一个内存区域
在这种情况下,如果执行指令
那么,处理器将把数据段寄存器DS 的内容(0x07c0)左移4 位,加上指令中指定的偏移地址(0x05),形成物理内存地址0x07c05,并将寄存器DL 中的内容传送到该处。
所以,源程序第13、14 行,通过传送指令将数据段寄存器DS 的内容设置为0x07c0。和以前一样,源程序第16、17 行,使附加段寄存器ES 的内容指向显示缓冲区所在的段0xb800。