x86 指令集全面解析
1. 引言
x86 指令集是计算机编程和系统开发中的重要部分。这里主要介绍常用的 32 位 x86 指令,不涉及系统模式指令以及通常仅在操作系统内核代码或保护模式设备驱动程序中使用的指令。
在指令描述中,每个指令会通过一系列框来描述其对 CPU 状态标志的影响。标志用单个字母标识,各符号含义如下:
|符号|含义|
|----|----|
|O|溢出|
|S|符号|
|P|奇偶性|
|D|方向|
|Z|零|
|C|进位|
|I|中断|
|A|辅助进位|
|1|设置标志|
|0|清除标志|
|?|可能将标志更改为未确定的值|
|(空白)|标志不变|
|*|根据与标志相关的特定规则更改标志|
例如,某个指令描述中的 CPU 标志图显示:溢出、符号、零和奇偶标志将更改为未知值;辅助进位和进位标志将根据相关规则修改;方向和中断标志不变。
2. 指令描述和格式
在 x86 指令中,源操作数和目的操作数遵循自然顺序,即第一个操作数是目的操作数,第二个是源操作数。以 MOV 指令为例:
MOV destination, source
单个指令可能有多种格式。以下是指令格式中常用符号的含义:
|符号|描述|
|----|----|
|reg|8 位、16 位或 32 位通用寄存器,如 AH、AL 等|
|reg8, reg16, reg32|按位数标识的通用寄存器|
|segreg|16 位段寄存器(CS、DS、ES、SS、FS、GS)|
|accum|AL、AX 或 EAX|
|mem|使用任何标准内存寻址模式的内存操作数|
|mem8, mem16, mem32|按位数标识的内存操作数|
|shortlabel|代码段中距离当前位置 -128 到 +127 字节内的位置|
|nearlabel|当前代码段中由标签标识的位置|
|farlabel|外部代码段中由标签标识的位置|
|imm|立即操作数|
|imm8, imm16, imm32|按位数标识的立即操作数|
|instruction|80x86 汇编语言指令|
在描述单个指令时,“x86” 表示该指令或其变体仅适用于 32 位 x86 系列处理器(从 Intel386 开始);“(80286)” 表示至少需要使用 Intel 80286 处理器。寄存器符号如 (E)CX、(E)SI 等用于区分使用 32 位寄存器的 x86 处理器和使用 16 位寄存器的早期处理器。
3. 非浮点指令集详情
非浮点指令集包含众多指令,以下是部分指令介绍:
-
AAA(加法后 ASCII 调整)
:在两个 ASCII 数字相加后调整 AL 中的结果。如果 AL > 9,结果的高位数字放入 AH,并设置进位和辅助进位标志。
- 指令格式:AAA
-
AAD(除法前 ASCII 调整)
:将 AH 和 AL 中的未压缩 BCD 数字转换为单个二进制值,为 DIV 指令做准备。
- 指令格式:AAD
-
AAM(乘法后 ASCII 调整)
:在两个未压缩 BCD 数字相乘后调整 AX 中的结果。
- 指令格式:AAM
-
AAS(减法后 ASCII 调整)
:在减法操作后调整 AX 中的结果。如果 AL > 9,AAS 递减 AH 并设置进位和辅助进位标志。
- 指令格式:AAS
-
ADC(带进位加法)
:将源操作数和进位标志加到目的操作数上,操作数必须大小相同。
- 指令格式:
- ADC reg,reg
- ADC reg,imm
- ADC mem,reg
- ADC mem,imm
- ADC reg,mem
- ADC accum,imm
-
ADD(加法)
:将源操作数加到目的操作数上,结果存储在目的操作数中,操作数必须大小相同。
- 指令格式:
- ADD reg,reg
- ADD reg,imm
- ADD mem,reg
- ADD mem,imm
- ADD reg,mem
- ADD accum,imm
-
AND(逻辑与)
:目的操作数的每一位与源操作数的对应位进行逻辑与操作。
- 指令格式:
- AND reg,reg
- AND reg,imm
- AND mem,reg
- AND mem,imm
- AND reg,mem
- AND accum,imm
下面是部分指令对标志影响的 mermaid 流程图:
graph LR
A[指令执行] --> B{是否影响标志}
B -- 是 --> C{影响哪些标志}
C -- 溢出 --> D[O 标志改变]
C -- 符号 --> E[S 标志改变]
C -- 奇偶性 --> F[P 标志改变]
C -- 零 --> G[Z 标志改变]
C -- 进位 --> H[C 标志改变]
C -- 辅助进位 --> I[A 标志改变]
B -- 否 --> J[标志不变]
4. 更多非浮点指令
- BOUND(检查数组边界,80286) :验证有符号索引值是否在数组边界内。在 80286 处理器上,目的操作数可以是包含要检查索引的任何 16 位寄存器,源操作数必须是 32 位内存操作数,其高字和低字包含索引值的上下边界。在 x86 处理器上,目的操作数可以是 32 位寄存器,源操作数可以是 64 位内存操作数。
-
指令格式:
- BOUND reg16,mem32
- BOUND r32,mem64
- BSF、BSR(位扫描,x86) :扫描操作数以找到第一个设置位。如果找到该位,清除零标志,并将目的操作数赋值为遇到的第一个设置位的位号(索引)。如果未找到设置位,ZF = 1。BSF 从位 0 向最高位扫描,BSR 从最高位向位 0 扫描。
-
指令格式(适用于 BSF 和 BSR):
- BSF reg16,r/m16
- BSF reg32,r/m32
- BSWAP(字节交换,x86) :反转 32 位目的寄存器的字节顺序。
- 指令格式:BSWAP reg32
- BT、BTC、BTR、BTS(位测试,x86) :将指定的位(n)复制到进位标志。目的操作数包含该位所在的值,源操作数指示该位在目的操作数中的位置。BT 将位 n 复制到进位标志;BTC 将位 n 复制到进位标志并对目的操作数中的位 n 取反;BTR 将位 n 复制到进位标志并清除目的操作数中的位 n;BTS 将位 n 复制到进位标志并设置目的操作数中的位 n。
-
指令格式:
- BT r/m16,imm8
- BT r/m16,r16
- BT r/m32,imm8
- BT r/m32,r32
- CALL(调用过程) :将下一条指令的位置压入栈中,并转移到目的位置。如果过程是近过程(在同一段中),仅压入下一条指令的偏移量;否则,压入段和偏移量。
-
指令格式:
- CALL nearlabel
- CALL mem16
- CALL farlabel
- CALL mem32
- CALL reg
- CBW(字节转换为字) :将 AL 中的符号位扩展到 AH 寄存器。
- 指令格式:CBW
- CDQ(双字转换为四字,x86) :将 EAX 中的符号位扩展到 EDX 寄存器。
- 指令格式:CDQ
- CLC(清除进位标志) :将进位标志清除为零。
- 指令格式:CLC
- CLD(清除方向标志) :将方向标志清除为零,字符串原语指令将自动递增 (E)SI 和 (E)DI。
- 指令格式:CLD
- CLI(清除中断标志) :将中断标志清除为零,禁用可屏蔽硬件中断,直到执行 STI 指令。
- 指令格式:CLI
- CMC(取反进位标志) :切换进位标志的当前值。
- 指令格式:CMC
- CMP(比较) :通过对目的操作数减去源操作数进行隐含减法来比较两者。
-
指令格式:
- CMP reg,reg
- CMP reg,imm
- CMP mem,reg
- CMP mem,imm
- CMP reg,mem
- CMP accum,imm
| 指令 | 对标志的影响 |
|---|---|
| BOUND | 通常不影响标志 |
| BSF、BSR | 可能影响 ZF 标志 |
| BSWAP | 不影响标志 |
| BT、BTC、BTR、BTS | 影响 C 标志 |
| CALL | 不影响标志(除了栈操作可能间接影响) |
| CBW、CDQ | 不影响标志 |
| CLC | C 标志置 0 |
| CLD | D 标志置 0 |
| CLI | I 标志置 0 |
| CMC | C 标志取反 |
| CMP | 影响 O、S、Z、P、C、A 标志 |
5. 继续深入非浮点指令
- CMPS、CMPSB、CMPSW、CMPSD(比较字符串) :比较由 DS:(E)SI 和 ES:(E)DI 寻址的内存中的字符串,通过隐含减法比较源和目的字符串。CMPSB 比较字节,CMPSW 比较字,CMPSD 在 x86 处理器上比较双字。(E)SI 和 (E)DI 根据操作数大小和方向标志的状态进行增减。如果方向标志设置,(E)SI 和 (E)DI 递减;否则递增。
-
指令格式(省略显式操作数格式):
- CMPSB
- CMPSW
- CMPSD
- CMPXCHG(比较并交换) :将目的操作数与累加器(AL、AX 或 EAX)进行比较。如果相等,将源操作数复制到目的操作数;否则,将目的操作数复制到累加器。
-
指令格式:
- CMPXCHG reg,reg
- CMPXCHG mem,reg
- CWD(字转换为双字) :将 AX 中的符号位扩展到 DX 寄存器。
- 指令格式:CWD
- DAA(加法后十进制调整) :在两个压缩 BCD 值相加后调整 AL 中的二进制和,将和转换为两个 BCD 数字。
- 指令格式:DAA
- DAS(减法后十进制调整) :将减法操作的二进制结果转换为 AL 中的两个压缩 BCD 数字。
- 指令格式:DAS
- DEC(递减) :从操作数中减去 1,不影响进位标志。
-
指令格式:
- DEC reg
- DEC mem
- DIV(无符号整数除法) :执行 8 位、16 位或 32 位无符号整数除法。如果除数是 8 位,被除数是 AX,商是 AL,余数是 AH;如果除数是 16 位,被除数是 DX:AX,商是 AX,余数是 DX;如果除数是 32 位,被除数是 EDX:EAX,商是 EAX,余数是 EDX。
-
指令格式:
- DIV reg
- DIV mem
下面是这些指令操作流程的 mermaid 流程图:
graph LR
A[指令选择] --> B{是否为字符串比较指令}
B -- 是 --> C[CMPS 系列指令]
C --> D[比较字符串并调整指针]
B -- 否 --> E{是否为比较交换指令}
E -- 是 --> F[CMPXCHG 指令]
F --> G[比较并交换值]
E -- 否 --> H{是否为转换指令}
H -- 是 --> I[CWD、DAA、DAS 指令]
I --> J[进行转换操作]
H -- 否 --> K{是否为递减指令}
K -- 是 --> L[DEC 指令]
L --> M[操作数减 1]
K -- 否 --> N[DIV 指令]
N --> O[执行除法操作]
6. 再看更多非浮点指令
- ENTER(创建栈帧,80286) :为接收栈参数并使用本地栈变量的过程创建栈帧。第一个操作数表示为本地栈变量保留的字节数,第二个操作数表示过程嵌套级别(对于 C、Basic 和 FORTRAN 必须设置为 0)。
- 指令格式:ENTER imm16,imm8
- HLT(暂停) :停止 CPU 执行,直到发生硬件中断(注意:必须使用 STI 指令设置中断标志才能使硬件中断发生)。
- 指令格式:HLT
- IDIV(有符号整数除法) :对 EDX:EAX、DX:AX 或 AX 执行有符号整数除法操作。通常在 IDIV 操作前使用 CBW 或 CWD 对被除数进行符号扩展。
-
指令格式:
- IDIV reg
- IDIV mem
- IMUL(有符号整数乘法) :对 AL、AX 或 EAX 执行有符号整数乘法。如果乘数是 8 位,被乘数是 AL,积是 AX;如果乘数是 16 位,被乘数是 AX,积是 DX:AX;如果乘数是 32 位,被乘数是 EAX,积是 EDX:EAX。如果 16 位积扩展到 AH,32 位积扩展到 DX,或 64 位积扩展到 EDX,则设置进位和溢出标志。
-
指令格式:
- 单操作数:
- IMUL r/m8
- IMUL r/m16
- IMUL r/m32
- 双操作数:
- IMUL r16,r/m16
- IMUL r16,imm8
- IMUL r32,r/m32
- IMUL r32,imm8
- IMUL r16,imm16
- IMUL r32,imm32
- 三操作数:
- IMUL r16,r/m16,imm8
- IMUL r16,r/m16,imm16
- IMUL r32,r/m32,imm8
- IMUL r32,r/m32,imm32
- IN(从端口输入) :从端口将字节或字输入到 AL 或 AX 中。源操作数是端口地址,可以是 8 位常量或 DX 中的 16 位地址。在 x86 处理器上,可以从端口将双字输入到 EAX 中。
-
指令格式:
- IN accum,imm
- IN accum,DX
- INC(递增) :将 1 加到寄存器或内存操作数上。
-
指令格式:
- INC reg
- INC mem
- INS、INSB、INSW、INSD(从端口输入到字符串,80286) :从端口输入由 ES:(E)DI 指向的字符串,端口号在 DX 中指定。对于每个接收到的值,(E)DI 的调整方式与 LODSB 等字符串原语指令相同。可以使用 REP 前缀。
-
指令格式:
- INS dest,DX
- REP INSB dest,DX
- REP INSW dest,DX
- REP INSD dest,DX
| 指令 | 功能描述 | 对标志的影响 |
|---|---|---|
| ENTER | 创建栈帧 | 通常不影响标志 |
| HLT | 暂停 CPU | 不影响标志 |
| IDIV | 有符号整数除法 | 可能影响多个标志 |
| IMUL | 有符号整数乘法 | 可能影响 C、O 等标志 |
| IN | 从端口输入 | 不影响标志 |
| INC | 递增操作 | 可能影响 O、S、Z、P、A 标志 |
| INS 系列 | 从端口输入到字符串 | 通常不影响标志 |
7. 浮点指令集详情
浮点指令集用于处理浮点运算,以下是部分浮点指令介绍:
-
F2XM1
:计算 2^x - 1,无操作数。
-
FABS
:取绝对值,清除 ST(0) 的符号位,无操作数。
-
FADD
:浮点加法,将目的和源操作数相加,结果存储在目的操作数中。
- 指令格式:
- FADD:将 ST(0) 加到 ST(1) 并弹出栈
- FADD m32fp:将 m32fp 加到 ST(0)
- FADD m64fp:将 m64fp 加到 ST(0)
- FADD ST(0),ST(i):将 ST(i) 加到 ST(0)
- FADD ST(i),ST(0):将 ST(0) 加到 ST(i)
-
FADDP
:浮点加法并弹出栈,执行与 FADD 相同的操作,然后弹出栈。
- 指令格式:FADDP ST(i),ST(0),将 ST(0) 加到 ST(i)
-
FIADD
:将整数转换为浮点并相加,将目的和源操作数相加,结果存储在目的操作数中。
- 指令格式:
- FIADD m32int:将 m32int 加到 ST(0)
- FIADD m16int:将 m16int 加到 ST(0)
-
FBLD
:加载二进制编码的十进制数,将 BCD 源操作数转换为双扩展精度浮点格式并压入栈。
- 指令格式:FBLD m80bcd,将 m80bcd 压入寄存器栈
下面是浮点加法指令流程的 mermaid 流程图:
graph LR
A[开始] --> B{选择指令}
B -- FADD --> C[执行加法操作]
C --> D{是否弹出栈}
D -- 是 --> E[弹出栈]
D -- 否 --> F[结束]
B -- FADDP --> G[执行加法并弹出栈]
G --> F
B -- FIADD --> H[转换整数并执行加法]
H --> F
8. 更多浮点指令
- FBSTP :存储 BCD 整数并弹出栈,将 ST(0) 中的值转换为 18 位压缩 BCD 整数,存储在目的操作数中,然后弹出寄存器栈。
- 指令格式:FBSTP m80bcd,将 ST(0) 存储到 m80bcd 并弹出栈
- FCHS :改变符号,对 ST(0) 的符号位取反,无操作数。
- FCLEX :清除异常,清除浮点异常标志(PE、UE、OE、ZE、DE、IE)、异常汇总状态标志(ES)、栈故障标志(SF)和 FPU 状态字中的忙标志(B)。FNINIT 执行相同操作但不检查未屏蔽的浮点异常。
- FCMOVcc :浮点条件移动,测试 EFLAGS 中的状态标志,如果给定测试条件为真,将源操作数(第二个操作数)移动到目的操作数(第一个操作数)。
-
指令格式:
- FCMOVB ST(0),ST(i):如果低于则移动
- FCMOVE ST(0),ST(i):如果相等则移动
- FCMOVBE ST(0),ST(i):如果低于或相等则移动
- FCMOVU ST(0),ST(i):如果无序则移动
- FCMOVNB ST(0),ST(i):如果不低于则移动
- FCMOVNE ST(0),ST(i):如果不相等则移动
- FCMOVNBE ST(0),ST(i):如果不低于或相等则移动
- FCMOVNU ST(0),ST(i):如果不无序则移动
- FCOM :比较浮点值,将 ST(0) 与源操作数比较,并根据结果设置 FPU 状态字中的条件码标志 C0、C2 和 C3。
-
指令格式:
- FCOM m32fp:将 ST(0) 与 m32fp 比较
- FCOM m64fp:将 ST(0) 与 m64fp 比较
- FCOM ST(i):将 ST(0) 与 ST(i) 比较
- FCOM:将 ST(0) 与 ST(1) 比较
- FCOMP 执行与 FCOM 相同操作并弹出栈,FCOMPP 执行相同操作并弹出栈两次。FUCOM、FUCOMP 和 FUCOMPP 与 FCOM、FCOMP 和 FCOMPP 相同,但检查无序值。
| 指令 | 功能 | 操作数 |
|---|---|---|
| FBSTP | 存储 BCD 整数并弹出栈 | m80bcd |
| FCHS | 改变符号 | 无 |
| FCLEX | 清除异常 | 无 |
| FCMOVcc | 浮点条件移动 | ST(0),ST(i) 等 |
| FCOM | 比较浮点值 | m32fp、m64fp、ST(i) 等 |
通过对这些 x86 指令集的详细解析,我们可以更深入地理解计算机底层的运算和操作机制,为编程和系统开发提供有力的支持。
超级会员免费看
99

被折叠的 条评论
为什么被折叠?



