第九章 混合16位和32位编码
本章主要说明一些不常用的地址格式和跳转指令,在写象保护模式下的操作系统代码时需要的混合段尺寸操作
代码,如16位的段试途修改一个32位的数据或在不同尺寸段间的跳转。
9.1 混合尺寸跳转
当写一个32位的系统时要用一个常用的混合尺寸指令:你在16位模式设置,如取内核,然后切换到保护模式中
跳到32位内核的起始地址。在一个全32位的OS中,这将需要混合指令,因为种种原因在它在纯16位代码运行前
和纯32位代码运行后。跳转必须指定一个48位的远地址,因为种种原因一个目标地址为32位的。然而,它必须
在一个16位的段中汇编。所以这样做:
jmp 0x1234:0x56789ABC;错的
将不工作,因为偏移部分的地址将被载成0x9ABC并跳到一个原始的16位远地址处。
Linux内核设置代码得到相似于as86用代码生成的指令,用DB指令。NASM可以更好些,但它自己会生成正确的
代码: jmp dword 0x1234:0x56789ABC;对的
DWORD前缀(严格的说,它应该跟在冒号后面,因为种种原因它定义了一个doubleword的偏移;但NASM将会接受
这种格式,因为两者都是明确的)将使偏移部分被做为far对待,假设你故意写一个从16位段到32位段的跳转。
你可以保佑和相反操作,从32位段到16位用一个WORD前缀:
jmp word 0x8765:0x4321 ;32到16位
如果WORD前缀在16位模式中被指定,或DWORD前缀在32位模式它们将被忽略,因为每一个会使NASM在它在的模式中操作。
92. 在不同尺寸段中定址
如果你的OS是16位和32位混和,或你正写一个DOS扩展,你想处理一些16位段和32位。这时,你将在16位段中
写一个访问在32位段中的数据,反之也是。
最简单的方法是保证你用一个地址寄存器,因为有效地址包含一个32位的寄存器将被变成32位地址 ,所以:
mov eax,offset_into_32_bit segment_specified_by_fs
mov dword [fs:eax],0x11223344
这样做不错,但如果你已经知道了你要的精确偏移,这就有些讨厌(因为它浪费了一个指令和一个寄存器)。x86
体系允许32位有效地址指定4字节的偏移,所以为什么NASM不能为这个目的生成最好的指令?它能,象在第
9.1节中,你只需要用DWORD前缀地址,并且为一个32位的地址:
mov dword [fs:dwrod my_offset],0x11223344
也象在第9.1节中一样,NASM不注意DWORD前缀是在段重载前还是后,所以关于一个美观的争论为:
mov dword [dword fs:my_offset],0x11223344
不要对DWORD前缀在方括号外感到迷惑,这是为了控制存在地址上的数据的尺寸 。在方括号里面的将控制地址
本身的长度。这两个很容易区分:
mov word [dword 0x12345678],0x9abc
这将移动16个位数据到用32位偏移指定的位置。你也可以指令WORD或DWORD前缀来使FAR前缀来表示跳转或
调用,例如:
call dword far [fs:word 0x4321]
这条指令包含了16位偏移指定的地址;它从中取出48位远指针(16位段和32位偏移),然后调用地址。
9.2 其它混合尺寸的指令
另外的访问数据的指令为字串指令(LODSx,STOSx等待)或XLATB指令。这些指令由于它们没有参数,在16位段中
汇编是时32位定址不是很容易。这是NASM用a16和a32前缀的原因。如果你在16位段中用LODSB编码但它支持
在32位段中访问一个字串,你可以从ESI中取出地址然后:
a32 lodsb
这个前缀使定址的尺寸为32位的,意味着LODSB从[DS:ESI]中取数而不是[DS:SI]。当写一个32位代码时要在
一个16位段中访问字串,可以用a16前缀。
a16和a32前缀可以用在NASM指令表中的任何指令前,但大多数指令在没有用前缀的时,也会生成正确的指令。
这个前缀对于隐式的定址是有用的:CMPSx,(见第A.24节),SCASX(见第A.229节),LODSx(见第A.117节),STOSx(见第
A.243节),MOVSx(见第A.137节),INSx(见第A.98节),OUTSx(见第A.149节)和XLATB(见第A.269节),不同的push和pop指令
也可以用a16或a32做前缀使SP或ESP中的一个成为堆栈指针。(PUSHA和POPF与PUSH和POP一样)在这种情况下,
堆栈段将与代码段为不一样的尺寸 。PUSH和POP,当用在32位模式的段寄存器中时,每次将处理4个字节,上
面的2个将忽略,而下面的2个将交给段寄存器处理。为了使用16位行为的段寄存器push和pop指令,你可以用操
作尺寸前缀o16:
o16 push ss
o16 push ds
这段代码将存一个doubleword的堆栈通过将两个段寄存器修改空间用压入堆栈和消费的方法。(你也可以用o32前
缀在16位模式中用,但这很少用。
第十章 常见问题
本章描述了用户在使用NASM所遇到的一个问题及解答。它也给出了一些NASM中的显示bugs的指令,如果你发
现它不在列表中时。
10.1常见问题
10.1.1 NASM生成的无效代码
我得到很多\'bug\'显示NASM生成了无效的或\'wrong\'的代码象指令ADD ESP,8。这是一个故意设计的特性:连到
预期的输出:NASM处理ADD ESP,8时将生成为32位偏移保存空间的指令。你需要用ADD ESP,BYTE 8如果你想要
让空间有效的指令。这不是bug:这是一个有歧义的特性并只是一种观点。
10.1.2我的跳转出界了
同样,人们抱怨当他们用跳转指令跳太远时,NASM报告\'short jump out of range\'而不是生成一个远跳转。这也是
预处理的一种,但事实是有更多的原因。NASM不能告诉代码所运行的处理器的类型;所以它不能决定它是在
386还是更高的机器运行生成Jcc NEAR类型的指令。另外它可心伤脑筋一个短JE指令代替出界的短JNE指令也就
是跳过JMP NEAR;这对低于386的处理器是一个敏感的解决方法,保龄球对有好的预期操作或用JNE NEAR的处
理器上却无效。所以这是由用户而不是编译器来决定应生成什么的指令。
10.1.3 ORG不能工作
人们用bin格式引导程序经常抱怨ORG不能象他们想的去工作:为了在512个字节后放一个0xAA55标志,他们的
用MASM代码如下:
ORG 0
;一些扇区代码
ORG 510
DW 0xAA55
而在NASM中用ORG定向符则不工作,在NASM中正确的方法是TIMES定向符:
ORG 0
;一些扇区代码
TIMES 510-($-$$) DB 0
DW 0xAA55
TIMES定向符将会插入适当的0到输出中使汇编大小为510个字节。这种方法也可以是如果你意外的将你的引导
扇区填满,NASM将在汇编时捕到错误并显示,所以你不必在运行时反汇编它来找错误。
10.14 TIMES不工作
用上面代码常见的问题为人们用下面的代码:
TIME 510-$ DB 0
这会使$成为一个纯数字,象510一样,它们间的区别为一个数字被传给了TIMES。NASM是一个模块式的编译器
:不同的单元被设计成可重用的和易分隔的,所以它们不需要交换信息。结果bin输出格式甚至用ORG定向符
来表明.text段为从0开始,而不把这个信息传给后面的表达式。所以从计算的观点看,$不是一个纯数字:它是
从一个段基址的偏移,然而在$和510之间不同的也不是纯数字,除了包括一个段基址。包含段基址的值的不能
被传到TIMES里做参数。象前面的面一样,应中下写:
TIMES 510-($-$$) DB 0
这里$和$$是从相同段基址的偏移,所以它们的区别为纯数字。这解决了问题并生成敏感的代码。
10.2 Bugs
我们从未出版过带有任何已知问题的NASM。这不会经常停在我们不很知道的地方。你发现的任何问题请向
报告。
请先读第2.2节,不要报告列在列表中的问题。(如果你觉得这个特性不好请说明原因,但不要写\'This is a buf\')。
然后 读一下10.1节也不要报告在列表中问题。
如果你报告了一个问题,请给我们下面的信息:
你用NASM的操作系统,DOS,Linux,NetBSD,Win16,Win32,VMS(外加的)或其它。
如果你在DOS或Win32下运行NASM,请告诉我们你是从DOS源文档中编译的执行文件还是用标准的二进制文档。
如果你用本地的二进制文件,请用标准的二进制文件重新生成一遍,使我们更容易修正问题。
你用的NASM的版本号,及你如何运行它的。给我们精确的命令行。和NASM环境变量的内容。
你所用程序的任何补充版本,你是如何执行它们的。如果问题在连接时出现,说明你用的连接器,及它的版本
和连接器的命令行。如果问题在连接用另外的编译器生成的目标文件出现的,说明是什么编译器,什么版本,
和你用的命令行。(如果你用的是一个IDC请用命令行版本的编译重新生成一下。)
如果有可能,请发给我们一分NASM出错的源文件。如果这引起版权问题(如你只能重生成限制发布代码的一个
问题)那么考虑以下两点:首先,我们保证任何发给我们源码的文件只用于调试NASM,如果我们修改完后,我们
将删除我们所有的拷贝。第二,我们接收很大的源码。文件越小越好。一个三行的文件将比上千行的代码更容易找到问题。(当然在大文件中有些问题突然出现)
问题的具体描述,\'它不能工作\'不是一个有用的描述!请精确的描述应该发生什么现象,不应该发生什么。例如:
\'\'NASM生成一条错误信息表示第3行有个错误实际在第5行\',\'NASM生成一个错误信息我认为 它根本不应该生成\'
,\'从源码生成的目标文件使我的连接器不能工作\';\'一个输出文件的第9个字节为66而我认为它应为77\'
如果你认为 输出文件从NASM出来是错的,请发给我们。这让我们来决定我们的NASM输出是否为同一个文件,
或问题是在于我们还是你们。我们可以处理的二进制文件为MIME附件,uuencode和BinHex.我们提供FTP站点
你可以上传有问题的文件,但寄给我们是最容易的。
另外信息或数据文件可能有用,如果NASM失败生成一个目标文件而TASM生成一个无错的等同文件。那么请把
两个文件都寄给我们。我们可以看出TASM的不同?