x86的指令集可分为以下4种:
通用指令
x87 FPU指令,浮点数运算的指令
SIMD指令,就是SSE指令
系统指令,写OS内核时使用的特殊指令
下面介绍一些通用的指令。指令由标识命令种类的助记符和操作数(operand)组成。例如move指令:
指令 | 操作数 | 描述 |
---|---|---|
movb | I/R/M, R/M | 从一个内存位置复制1个字节(8位)大小的数据到另外一个内存位置 |
movw | I/R/M, R/M | 从一个内存位置复制2个字节(16位)大小的数据到另外一个内存位置 |
movl | I/R/M,R/M | 从一个内存位置复制1个字(32位,4字节)大小的数据到另外一个内存位置 |
movq | I/R/M,R/M | 从一个内存位置复制1个双字(64位,8字节)大小的数据到另外一个内存位置 |
movl为助记符。助记符有后缀,表示作为操作数的对象的数据大小。b,w,l,q分别表示8位、16位、32位和64位的大小。
立即数(I)、寄存器(R)或内存地址(M),
在x86的汇编语言中,采用内存位置的操作数最多只能出现一个,例如不可能出现mov M,M指令。
通用寄存器中每个操作都可以有一个字符的后缀,表明操作数的大小,如下表所示。
C声明 | 通用寄存器后缀 | 大小(字节) |
---|---|---|
char | b | 1 |
short | w | 2 |
(unsigned) int / long / char* | l | 4 |
float | s | 4 |
double | l | 5 |
long double | t | 10/12 |
如果在研究HotSpot VM虚拟机的汇编遇到了callq,pushq等指令时,后缀就是表示了操作数的大小。
汇编根据编译器的不同,有2种书写格式:
(1)Intel : Windows派系
(2)AT&T: Unix派系
下面我们以 AT&T 风格介绍一些常见指令:
注意AT&T汇编的第1个操作数在前,第2个操作数在后。
mov指令:
movl %ecx,%eax
movl (%ecx),%eax
第一条指令将寄存器ecx中的值复制到eax寄存器;
第二条指令将ecx寄存器中的数据作为地址访问内存,并将内存上的数据加载到eax寄存器中。
cmov指令(支持带条件的mov指令):cmovxx
其中xx代表一个或者多个字母,这些字母表示将触发传送操作的条件。条件取决于 eflags寄存器的当前值。eflags寄存器如下图所示。
其中与cmove指令相关的eflags寄存器中的位有:
CF(数学表达式产生了进位或者借位)
OF(整数值无穷大或者过小)
PF(寄存器包含数学操作造成的错误数据)
SF(结果为正不是负)
ZF(结果为零)。
下表为无符号条件传送指令:
指令对 | 描述 | eflags状态 |
---|---|---|
cmova/cmovnbe | 大于/不小于或等于 | (CF或ZF)=0 |
cmovae/cmovnb | 大于或者等于/不小于 | CF=0 |
cmovnc | 无进位 | CF=0 |
cmovb/cmovnae | 大于/不小于或等于 | CF=1 |
cmovc | 进位 | CF=1 |
cmovbe/cmovna | 小于或者等于/不大于 | (CF或ZF)=1 |
cmove/cmovz | 等于/零 | ZF=1 |
cmovne/cmovnz | 不等于/不为零 | ZF=0 |
cmovp/cmovpe | 奇偶校验/偶校验 | PF=1 |
cmovnp/cmovpo | 非奇偶校验/奇校验 | PF=0 |
举个例子如下:
movl value,%ecx
cmp %ebx,%ecx
cmova %ecx,%ebx
第一条指令将vlaue数值加载到ecx寄存器中
第二条指令cmp指令比较ecx和ebx这两个寄存器中的值,用ecx减去ebx然后设置eflags
第三条指令 如果ecx的值大于ebx,使用cmova指令设置ebx的值为ecx中的值
lea指令:lea M,R
lea(Load Effective Address)指令将地址加载到寄存器。
lea计算源操作数的实际地址,并把结果保存到目标操作数,而目标操作数必须为通用寄存器
举例如下:
movl 4(%ebx),%eax
leal 4(%ebx),%eax
第一条指令表示将ebx寄存器中存储的值加4后得到的结果作为内存地址进行访问,并将内存地址中存储的数据加载到eax寄存器中。
第二条指令表示将ebx寄存器中存储的值加4后得到的结果作为内存地址存放到eax寄存器中。
jmp指令:
jmp指令将程序无条件跳转到操作数指定的目的地址。jmp指令可以视作设置指令指针(eip寄存器)的指令。目的地址也可以是星号后跟寄存器的栈,这种方式为间接函数调用。例如:
jmp *%eax
将程序跳转至eax所含地址。
Jcc指令: Jcc 目的地址
其中cc指跳转条件,如果为真,则程序跳转到目的地址;否则执行下一条指令。相关的条件跳转指令如下表所示。
enter指令:
通过初始化ebp和esp寄存器来为函数建立函数参数和局部变量所需要的栈帧。相当于
push %rbp // 保存当前栈帧A的栈底
mov %rsp,%rbp // 把当前栈帧A的栈顶,作为新栈帧B的栈底
leave指令:
leave通过恢复ebp与esp寄存器来移除使用enter指令建立的栈帧。相当于
mov %rbp, %rsp // 把当前栈帧B的栈底,作为之前栈帧A的栈顶
pop %rbp// 弹出之前栈帧A的栈底
指令的格式如下:
int I
引起给定数字的中断。这通常用于系统调用以及其他内核界面。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。