兰陵月 发表于 2017-11-13 21:18:02

X86汇编语言-从实模式到保护模式—笔记(17)-第10章 x86处理器编程架构(2)

本帖最后由 兰陵月 于 2017-12-10 20:33 编辑

10.332位模式的指令系统
【10.3.132位处理器的寻址方式】
32处理器出现之后,寄存器和偏移地址的宽度都扩展了,相应地,要继续扩展原有的寻址方式。但是,原有的16位方案已经成型,再进行修补是非常困难的。一个可行的解决方案是,让16位指令和32位指令共用相同的指令码,但通过不同的指令前缀,结合处理器当前的运行状态来决定该指令的寻址方式。
如处理器运行在16位模式,如果没有指令前缀0x66,则认为指令是传统的16位寻址方式,若有指令前缀0x66,则指令是新的32位寻址方式。如果处理器当前运行在32位模式下且没有指令前缀0x66,则视为默认的32位寻址方式,否则就是传统的16位寻址方式。
32位模式下,默认使用32位宽度的寄存器。如:
moveax,ebx
如果指令中使用了立即数,那么,该数值默认是32位的:
movecx,0x55;0x55=0x00000055
还有,如果指令中的操作数是指向内存单元的地址,那么,该地址默认是32位的段内偏移地址,或者叫段内偏移量:
movedx,;mem是一个32位的段内偏移地址
这就是说,如果指令中包含了内存地址操作数,那么,它必然默认地是一个32位的有效地址。通过有效地址,可以间接取得32位的实际操作数。指定有效地址可以使用全部的32通用寄存器作为基址寄存器。同时,还可以再加上一个除ESP之外的32位通用寄存器作为变址寄存器。变址寄存器还允许乘以1、2、4或者8作为比例因子。最后,还允许加上一个8位或者32位的偏移量。如下图:

在16位模式下,内存寻址方式的操作数不允许使用栈指针寄存器SP。比如“mov ax,”就是非法的。但是,在32位模式下,允许在内存操作数中使用栈指针寄存器ESP。比如“moveax,”是合法的。
【10.3.2操作数大小的指令前缀】
16位处理器时代发源的指令编码格式如下图:

每一条处理器指令都可以拥有前缀,比如重复前缀(REP/REPE/REPNE)、段超越前缀(比如ES:)、总线封锁前缀(LOCK)等。前缀是可选的,每个前缀的长度是1字节,每条指令可以有1~4个前缀,或者不使用前缀。
前缀(如果有的话)的后面是操作码部分,指示执行什么样的操作,比如传送、加法、减法、乘法、除法、移位等。根据指令的不同,操作码的长度是1~3字节。同时,操作码还可以用来指示操作的字长,即数据宽度是字节还是字。
操作码之后是操作数类型和寻址方式部分。这部分是可选的,简单的指令不包含这一部分,稍微复杂一点的指令,这一部分只有1字节;最复杂的指令,可能有2字节。这部分给出了指令的寻址方式,以及寄存器的类型(用的是哪个寄存器)。
指令的最后是立即数和偏移量。如果指令中使用了立即数,那么立即数就在这一部分给出;如果指令使用了带偏移量的寻址方式,那么偏移量也在这部分出现。取决于具体的指令,立即数可以是1、2或者4字节,偏移量部分与此相同。
32位处理器出现之后做了相应修改,主要是扩展了数据的宽度,其他都保持不变。但是这也带来了一些问题,比如相同的机器指令,在16位模式下和32位模式下的解释和执行效果是不同的。但是,别忘了,32位处理器可以执行16位的程序,包括实模式和16位保护模式。为此,在16位模式下,处理器把所有指令都看成16位的。
当处理器在16位模式下运行时,也可以使用32位的寄存器,执行32位的运算。为此,必须使用指令前缀0x66来临时改变这种默认状态,因为同一个指令码,在16位模式下和32位模式下具有不同的解释。相反地,如果处理器运行在32位模式下,那么,处理器认为指令的操作码都是32位的,如果你加了前缀,这个前缀就用来指示指令是16位的。因此,指令前缀0x66具有反转当前默认操作数大小的作用。
编译器提供了伪指令bits,用于指明其后的指令应该被编译成16位的,还是32位的。用法为“bits 16/32”或“”。
最后,16位模式是默认的编译模式。如果没有指定指令的编译模式,则默认是“bits 16”。
【10.3.3一般指令的扩展】
接后详细篇幅说明。
页: [1]
查看完整版本: X86汇编语言-从实模式到保护模式—笔记(17)-第10章 x86处理器编程架构(2)