x64 架构机器指令全解析
1. x64 架构指令概述
x64 架构如今拥有超 1000 条机器指令。不过别慌,很多指令仅在操作系统的保护模式下使用,还有大量用于实现浮点运算(因篇幅限制暂不展开),另有不少是用于快速加密和解密等特定功能的专用指令。
若想获取更完整权威的指令参考,可访问英特尔指令集文档: https://software.intel.com/en - us/download/intel - 64 - and - ia - 32 - architectures - sdm - combined - volumes - 1 - 2a - 2b - 2c - 2d - 3a - 3b - 3c - 3d - and - 4 ,该 PDF 文档长达 5060 页。若觉得查阅不便,也可访问 www.felixcloutier.com/x86/index.html ,此网站便于你查找所需指令,无需下载庞大文档。
2. x64 中移除的指令
在 x86 CPU 发展历程中,各代都会增减指令。以下是一些在 32 位时代学过但在 x64 中不再可用的常见指令:
- BCD 数学指令 :AAA、AAS、AAD、AAM、DAA 和 DAS。这些指令数十年前就不再必要,还占用二进制指令编码空间。
- 全压栈和全出栈指令 :PUSHA、POPA、PUSHAD 和 POPAD。如今有 16 个通用寄存器,每个 8 字节,一次移动大量数据不便,现在需逐个压栈和出栈寄存器。
- JCXZ 指令 :当 CX = 0 时跳转,这是古老的 16 位指令,其功能现由 JRCXZ(当 RCX = 0 时跳转)和 JECXZ(当 ECX = 0 时跳转)实现。
- PUSHFD 指令 :将 EFLAGS 寄存器压入栈,现用 PUSHFQ 压入 RFLAGS 寄存器。
- POPFD 指令 :从栈顶弹出双字(32 位)到 EFLAGS 寄存器,现用 POPFQ 弹出四字(64 位)到 RFLAGS 寄存器。
3. 标志位结果
每条指令都有标志位总结,示例如下:
O D I T S Z A P C OF: 溢出标志 TF: 陷阱标志 AF: 辅助进位标志
F F F F F F F F F DF: 方向标志 SF: 符号标志 PF: 奇偶标志
* ? ? * * * * * IF: 中断标志 ZF: 零标志 CF: 进位标志
这里涵盖了 9 个重要标志位:
- OF(溢出标志) :结果太大无法存入目标操作数时置 1。
- DF(方向标志) :由 STD 置 1,由 CLD 清 0。
- IF(中断标志) :由 STI 置 1,由 CLI 清 0,用户空间编程中不用,可忽略。
- TF(陷阱标志) :用于调试器,用户空间编程中不用,可忽略。
- SF(符号标志) :结果符号使目标操作数为负时置 1。
- ZF(零标志) :操作结果为 0 时置 1,非 0 时清 0。
- AF(辅助进位标志) :用于 4 位 BCD 数学运算,x64 中因 BCD 指令不可用,此标志不再使用。
- PF(奇偶标志) :结果低字节中 1 的个数为偶数时置 1,为奇数时清 0,主要用于数据通信应用。
- CF(进位标志) :加法或移位操作结果超出目标操作数时置 1,否则清 0,可通过 STC 手动置 1,通过 CLC 手动清 0。
部分指令会使某些标志位未定义,用问号表示,在标志位未通过其他指令明确前,勿测试或使用其状态。
4. 大小指定符
在汇编语言中访问内存数据时,存在确定操作内存数据大小的问题。例如 inc [rdi + 4] ,无法判断是对字节、字、双字还是四字进行递增操作,NASM 会报错。
NASM 识别 BYTE、WORD、DWORD 和 QWORD 等大小指定符,分别表示 8、16、32 和 64 字节。如 inc qword [rdi + 4] 明确是对 64 位值递增。
对于单操作数的 x64 指令(如 INC、DEC、NOT、NEG、SHR、SHL、ROR 和 ROL),操作内存时需大小指定符。对于双操作数指令:
- 若一个操作数是内存访问,另一个是寄存器,NASM 会根据寄存器大小推断访问大小,如 mov [rdi + rbx * 8], rcx ,因 RCX 是 64 位寄存器,会将 64 位数据从 RCX 移到 [RDI + RBX * 8] 开始的 8 字节处。
- 若一个操作数在内存,另一个是常量,需大小指定符,如 mov word [rdi], 42 表示将 42 复制到 RDI 地址开始的两个字节内存中。
5. 部分指令索引
| 指令 | 参考页 | 文本页 | CPU |
|---|---|---|---|
| ADC | |||
| ADD | |||
| AND | |||
| BT | 386+ | ||
| CALL | |||
| CLC | 此处仅提及 | ||
| CLD | |||
| CMP | |||
| DEC | |||
| INC | |||
| INT | |||
| IRET | |||
| J? | |||
| JECXZ | x64+ | ||
| JMP | |||
| JRCXZ | x64+ | ||
| LEA | |||
| LOOP | |||
| LOOPNZ/LOOPNE | |||
| LOOPZ/LOOPE | |||
| MOV | |||
| MUL | |||
| NEG | |||
| NOP | 此处仅提及 | ||
| NOT | |||
| OR | |||
| POP | |||
| POPF | |||
| POPFQ | x64+ | ||
| PUSH | |||
| PUSHF | |||
| PUSHFQ | x64+ | ||
| RET | |||
| ROL | |||
| ROR | |||
| SBB | 此处仅提及 | ||
| SHL | |||
| SHR | |||
| STC | 此处仅提及 | ||
| STD | |||
| STOS | |||
| SUB | |||
| SYSCALL | x64+ | ||
| XCHG | |||
| XLAT | |||
| XOR |
6. 部分指令详解
6.1 ADC:带进位的算术加法
- 受影响标志位 :
O D I T S Z A P C OF: 溢出标志 TF: 陷阱标志 AF: 辅助进位标志
F F F F F F F F F DF: 方向标志 SF: 符号标志 PF: 奇偶标志
* * * * * * IF: 中断标志 ZF: 零标志 CF: 进位标志
- 合法形式 :
ADC r/m8,i8
ADC r/m16,i16
ADC r/m32,i32 386+
ADC r/m64,i32 x64+ 注意: ADC r/m64X,i64 无效!
ADC r/m8,r8
ADC r/m16,r16
ADC r/m32,r32 386+
ADC r/m64,r64 x64+
ADC r/m16,i8
ADC r/m32,i8
ADC r/m64,i8 x64+
ADC r8,r/m8
ADC r16,r/m16
ADC r32,r/m32 386+
ADC r64,r/m64 x64+
ADC AL,i8
ADC AX,i16
ADC EAX,i32 386+
ADC RAX,i32 x64+ 注意: ADC RAX,i64 无效!
- 示例 :
ADC BX,DI
ADC EAX,5
ADC AX,0FFFFH
ADC AL,42H
ADC RBP,17H
ADC QWORD [RBX+RSI+Inset],5
- 说明 :ADC 将源操作数和进位标志加到目标操作数,结果替换目标操作数,是算术加法,进位用于多精度加法。若结果无法存入目标操作数,进位标志置 1。
6.2 ADD:算术加法
- 受影响标志位 :
O D I T S Z A P C OF: 溢出标志 TF: 陷阱标志 AF: 辅助进位标志
F F F F F F F F F DF: 方向标志 SF: 符号标志 PF: 奇偶标志
* * * * * * IF: 中断标志 ZF: 零标志 CF: 进位标志
- 合法形式 :
ADD r/m8,i8
ADD r/m16,i16
ADD r/m32,i32 386+
ADD r/m64,i32 x64+ 注意: ADD r/m64,i64 无效!
ADD r/m16,i8
ADD r/m32,i8 386+
ADD r/m64,i8 x64+
ADD r/m8,r8
ADD r/m16,r16
ADD r/m32,r32 386+
ADD r/m64,r64 x64+
ADD r8,r/m8
ADD r16,r/m16
ADD r32,r/m32 386+
ADD r64,r/m64 x64+
ADD AL,i8
ADD AX,i16
ADD EAX,i32 386+
ADD RAX,i32 x64+ 注意: ADD RAX,i64 无效!
- 示例 :
ADD BX,DI
ADD AX,0FFFFH
ADD AL,42H
ADD [EDI],EAX
AND QWORD [RAX],7BH
- 说明 :ADD 将源操作数加到目标操作数,结果替换目标操作数,是算术加法,不考虑进位标志(考虑进位用 ADC)。若结果无法存入目标操作数,进位标志置 1。
6.3 AND:逻辑与
- 受影响标志位 :
O D I T S Z A P C OF: 溢出标志 TF: 陷阱标志 AF: 辅助进位标志
F F F F F F F F F DF: 方向标志 SF: 符号标志 PF: 奇偶标志
* * * ? * * IF: 中断标志 ZF: 零标志 CF: 进位标志
- 合法形式 :
AND r/m8,i8
AND r/m16,i16
AND r/m32,i32 386+
AND r/m64,i32 x64+ 注意: AND r/m64,i64 无效!
AND r/m16,i8
AND r/m32,i8 386+
AND r/m64,i8 x64+
AND r/m8,r8
AND r/m16,r16
AND r/m32,r32 386+
AND r/m64,r64 x64+
AND r8,r/m8
AND r16,r/m16
AND r32,r/m32 386+
AND r64,r/m64 x64+
AND AL,i8
AND AX,i16
AND EAX,i32 386+
AND RAX,i32 x64+ 注意: AND RAX,i64 无效!
- 示例 :
AND BX,DI
AND EAX,5
AND AX,0FFFFH
AND AL,42H
AND [BP+SI],DX
AND QWORD [RDI],42
AND QWORD [RBX],0B80000H
- 说明 :AND 对两个操作数逐位进行逻辑与运算,结果替换目标操作数。该操作使辅助进位标志未定义,CF 和 OF 清 0,其他受影响标志位根据运算结果设置。
6.4 BT:位测试
- 受影响标志位 :
O D I T S Z A P C OF: 溢出标志 TF: 陷阱标志 AF: 辅助进位标志
F F F F F F F F F DF: 方向标志 SF: 符号标志 PF: 奇偶标志
* IF: 中断标志 ZF: 零标志 CF: 进位标志
- 合法形式 :
BT r/m16,r16 386+
BT r/m32,r32 386+
BT r/m64,r64 x64+
BT r/m16,i8 386+
BT r/m32,i8 386+
BT r/m64,i8 x64+
- 示例 :
BT AX,CX
BT EAX,EDX
BT RAX,5
BT [RAX+RDX],RCX
- 说明 :BT 将左操作数的指定单一位复制到进位标志,可用于测试或通过移位/旋转指令反馈。右操作数指定复制的位,不改变操作数。若右操作数是 8 位立即值,指定复制的位编号;若超出左操作数大小,按左操作数大小取模。若右操作数不是立即值,还指定内存引用偏移,较复杂,可查阅完整汇编语言参考。
6.5 CALL:调用过程
- 受影响标志位 :
O D I T S Z A P C OF: 溢出标志 TF: 陷阱标志 AF: 辅助进位标志
F F F F F F F F F DF: 方向标志 SF: 符号标志 PF: 奇偶标志
<无> IF: 中断标志 ZF: 零标志 CF: 进位标志
- 合法形式 :
CALL d16 x64 中无效
CALL d32 x64 之前无效
CALL r/m16 x64 中无效
CALL r/m32 x64 中无效
CALL r/m64 x64 之前无效
- 示例 :
CALL DoSomething
CALL RAX
CALL QWORD [EBX+ECX+16]
- 说明 :CALL 将控制权转移到过程地址,转移前将下一条指令地址压入栈,以便 RET 指令返回。除调用定义的标签,还可转移到 64 位通用寄存器或内存中的地址,CALL m64 用于创建过程地址跳转表。d32 是 32 位无符号位移,在 64 位长模式下符号扩展为 64 位。CALL 还有与操作系统保护机制相关的变体,可查阅高级资料。
7. 指令操作数说明
为更好理解指令,对操作数的含义进行说明:
| 操作数 | 含义 |
| — | — |
| m8 | 8 位内存数据 |
| m16 | 16 位内存数据 |
| m32 | 32 位内存数据 |
| m64 | 64 位内存数据 |
| i8 | 8 位立即数据 |
| i16 | 16 位立即数据 |
| i32 | 32 位立即数据 |
| i64 | 64 位立即数据 |
| d8 | 8 位有符号位移 |
| d16 | 16 位有符号位移 |
| d32 | 32 位无符号位移(无 64 位位移) |
| r8 | AL、AH、BL、BH、CL、CH、DL、DH |
| r16 | AX、BX、CX、DX、BP、SP、SI、DI |
| r32 | EAX、EBX、ECX、EDX、EBP、ESP、ESI、EDI |
| r64 | RAX、RBX、RCX、RDX、RBP、RSP、RSI、RDI、R8、R9、R10、R11、R12、R13、R14、R15 |
8. 指令使用流程示例
以 ADD 指令为例,展示其在实际使用中的流程:
graph TD;
A[开始] --> B[确定操作数类型和大小];
B --> C{操作数是否符合合法形式};
C -- 是 --> D[执行 ADD 指令];
C -- 否 --> E[调整操作数或指令形式];
E --> C;
D --> F[检查标志位状态];
F --> G[根据标志位进行后续操作];
G --> H[结束];
9. 指令使用注意事项
- 大小指定符使用 :在操作内存数据时,务必根据指令和操作数情况正确使用大小指定符,避免 NASM 报错。例如单操作数指令操作内存时,一定要添加合适的大小指定符。
- 标志位状态 :部分指令会影响标志位状态,有些还会使标志位未定义。在使用标志位进行判断或操作前,要确保标志位处于确定状态。
- 指令兼容性 :不同 CPU 版本对指令的支持不同,如
386+表示从 386 及以上 CPU 支持,x64+表示从 x64 及以上 CPU 支持,使用时要根据实际 CPU 情况选择合适的指令。
10. 常见指令使用场景分析
- 算术运算指令(ADC、ADD) :在进行多精度加法时,可使用
ADC指令结合进位标志实现;普通加法运算则使用ADD指令。例如在处理大整数加法时,可利用ADC逐位相加并处理进位。 - 逻辑运算指令(AND) :常用于位掩码操作,如提取特定位信息。例如
AND EAX, 0x000000FF可提取 EAX 寄存器的低 8 位。 - 位测试指令(BT) :可用于检查某个二进制数中特定位置的位状态,如
BT RAX, 5可检查 RAX 寄存器中第 5 位的状态。 - 调用过程指令(CALL) :在程序中调用子过程或函数时使用,可实现代码的模块化和复用。例如
CALL DoSomething可调用名为DoSomething的子过程。
11. 总结
x64 架构的机器指令丰富多样,理解和掌握这些指令对于编写高效的汇编语言程序至关重要。通过了解指令的功能、合法形式、受影响标志位以及操作数的含义,能够正确使用指令并处理各种情况。同时,要注意大小指定符的使用、标志位状态和指令兼容性等问题,根据不同的使用场景选择合适的指令,以实现程序的特定功能。在实际编程中,可结合具体需求,灵活运用这些指令,提高程序的性能和可维护性。
超级会员免费看
3012

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



