Prefixes!
http://www.luocong.com/learningopcode/doc/3._Prefixes_-_Part_I.htm
OpCode mnemonic
40 INC EAX
66 40 INC AX
我们可以看到,40表示的是INC EAX,66 40表示的是INC AX,两者的分别在于:前者的操作数是32位的(EAX),而后者是16位的(AX)。
从OpCode的角度来看,后者比前者多了一个66,就导致了不同的结果,唔……Intel x86规定:
66是一个Prefix,我们把Prefix翻译为前缀,所谓前缀,就是与code进行组合用以产生出某些变化形式的“东西.
请记住这6个域
Prefixes
code
ModR/M
SIB
Displacement
Immediate
Prefixes的几个特性
1. 它是唯一的一个可能出现在code之前的域。
2. 所有的Prefixes都只有1个字节。
3. 在一个OpCode中可能会有多个Prefixes。
看看刚才提到过的prefix 66,这个prefix的意思是“切换默认的操作数的大小”。
例如在有的系统中有2种默认的操作数大小:16位和32位。操作数有可能会被写成16位或者32位,唯一的区分方法是看它有没有prefix 66。
OpCode mnemonic
66 AD LODSW
AD LODSD
依然假设默认的操作数是32位的,有没有发现什么不寻常的地方?
LODSW和LODSD的code域是一样的——都是AD!其实,LODSW和LODSD这两个指令是同一个指令,只不过它们的操作数大小不一样——LODSW使用了2个字节(16位)的WORD作为操作数,而LODSD则使用了4个字节(32位)的DWORD作为操作数。
如果默认的操作数大小是WORD(16位),那么切换后就是DWORD(32位);反之,如果默认的操作数大小是DWORD(32位),那么切换后就是WORD(16位)。
(在32位的系统中, 66代表使用16位, 而在16位的系统中, 66代表使用32位).
切记!Prefixes 66就像一个触发器一样,起的作用就是进行切换。
让我们再来看一个特例:
B0 FF MOV AL, 0FF
8A C1 MOV AL, CL
看清楚了吗?现在的操作数是AL和CL,加上prefix 66后会如何?
66 B0 FF MOV AL, 0FF
66 8A C1 MOV AL, CL
Faint!没有任何变化!
为什么呢? 也许并不是所有情况下的操作数大小都可以随意改变的。假如这个改变是不允许的,那么它就会被忽略。
为了证实这个猜想,让我们来看看下一个更有趣的例子:
prefix F3(rep)的作用是让CPU对随后的指令循环执行ecx(cx)次,指令INC EAX的OpCode是40,好,如果我们想连续执行3次INC EAX的话,应该怎么样呢?
也许你会想当然地认为应该这样写:
xor eax, eax
mov ecx, 3
rep inc eax
实际上!并不是这样!这样的程序的运行结果是:
没有任何异常(exception)产生。
最后eax = 1,这意味着prefix F3并没有起作用——它被忽略了。
现在我们可以证实之前的想法:
如果Prefixes不能对随它之后的OpCode起作用,那么它就会被忽略。
再回忆一下之前提到的三个特性:
Prefixes是唯一的一个可能出现在code之前的域。
所有的Prefixes都只有1个字节。
在一个OpCode中可能会有多个Prefixes。
前面两点应该比较容易理解,让我们来看看第3点是什么意思。
如果想得到下面的指令:
REP LODSW
它的OpCode将会是:
66 F3 AD
解释如下:
66 AD:LODSW
F3: REP
都是前面讲过的内容,不难吧?只是组合起来使用罢了。
不过……细心的读者可能会问:为什么要把66放在第一位,把F3放在第二位呢?把它们的位置调换一下行不行?答案是:行!事实上它也可以写成:
F3 66 AD
效果是一样的!
Prefixes的特性
一个OpCode中可以有多个Prefixes。
如果有多个Prefixes,那么它们的顺序可以打乱,不会有任何问题。
最后,我们还可以得出一个推论:
由于每个Prefixes会多占用1个字节,所以也必定会导致处理器多使用一个指令周期进行解码——无论在时间还是空间上都会造成浪费。因此,我们应该权衡在哪些场合才使用Prefixes,如非必要,应该减少对它的使用。