计算机组成原理(17) 第三章 - 常用的X86 汇编指令

1. 常用的X86 汇编指令

x86 汇编指令集包含通用数据操作、控制转移、内存访问、系统调用等类别,以下是最常用的核心指令,按功能分类整理,兼顾 16 位 / 32 位 / 64 位架构的通用场景:

一、数据传输指令

用于寄存器、内存、立即数之间的数据搬运,是最基础的指令类型。

指令 功能说明 示例
MOV 数据传送(寄存器→寄存器、立即数→寄存器、内存→寄存器等) MOV EAX, EBX(EBX→EAX)MOV [ESI], 100(立即数 100 存入 ESI 指向的内存)
PUSH 将数据压入栈顶 PUSH EBP(EBP 入栈)
POP 从栈顶弹出数据到寄存器 / 内存 POP ESP(栈顶数据→ESP)
LEA 加载有效地址(计算内存地址并送入寄存器,不访问内存) LEA EAX, [EBX+ECX*4](计算 EBX+4×ECX 地址→EAX)
XCHG 交换两个操作数的数据 XCHG EAX, EDX(EAX 与 EDX 交换)
MOVSX/MOVZX 带符号 / 零扩展传送(如 8 位→32 位,保持符号位 / 补零) MOVSX EAX, BL(BL(8 位)带符号扩展→EAX(32 位))

二、算术运算指令

处理加减乘除、比较等数值计算,影响标志寄存器(CF、ZF、SF 等)。

指令 功能说明 示例
ADD 加法运算 ADD EAX, 5(EAX = EAX + 5)
SUB 减法运算 SUB EBX, ECX(EBX = EBX - ECX)
INC/DEC 自增 1 / 自减 1 INC EDX(EDX += 1)DEC [ESI](ESI 指向的内存值 - 1)
MUL/IMUL 无符号乘法 / 有符号乘法(8/16/32 位,结果存 AX/DX:AX/EDX:EAX) MUL BL(AX = AL × BL,无符号)IMUL ECX, EDX, 2(ECX = EDX × 2,有符号)
DIV/IDIV 无符号除法 / 有符号除法(被除数存 AX/DX:AX/EDX:EAX,商 / 余数存 AX/DX 等) DIV CX(DX:AX ÷ CX,商 AX,余数 DX,无符号)
CMP 比较运算(SUB 但不保存结果,仅影响标志位) CMP EAX, 0(EAX 与 0 比较,设置 ZF 等标志)
NEG 取负(按位取反 + 1) NEG EBX(EBX = -EBX)

三、逻辑运算与位操作指令

处理按位运算、移位、逻辑判断,常用于位掩码、条件判断。

指令 功能说明 示例
AND/OR/XOR 按位与 / 或 / 异或 AND EAX, 0xFF(EAX 与 0xFF 按位与)XOR EBX, EBX(EBX 清零)
NOT 按位取反 NOT ECX(ECX 按位取反)
SHL/SHR 逻辑左移 / 逻辑右移(右侧补 0) SHL EDX, 2(EDX 左移 2 位,乘 4)
SAR 算术右移(左侧补符号位) SAR EAX, 1(EAX 带符号右移 1 位,除 2)
TEST 按位与测试(同 AND 但不保存结果,仅设标志位) TEST EAX, 1(判断 EAX 奇偶,结果影响 ZF)

四、控制转移指令

改变程序执行流程(分支、循环、调用),依赖标志寄存器或直接跳转。

指令 功能说明 示例
JMP 无条件跳转 JMP label(跳转到 label 标签)
JE/JZ 相等 / 零标志置位时跳转(CMP 结果为 0) JE exit(若相等则跳 exit)
JNE/JNZ 不相等 / 零标志未置位时跳转 JNE loop(若不等则跳 loop)
JG/JGE 有符号数大于 / 大于等于时跳转 JG greater(EAX>EBX 则跳 greater)
JL/JLE 有符号数小于 / 小于等于时跳转 JLE less_eq(EAX≤EBX 则跳 less_eq)
JA/JAE 无符号数大于 / 大于等于时跳转 JA above(EAX>EBX 无符号则跳 above)
JB/JBE 无符号数小于 / 小于等于时跳转 JB below(EAX<EBX 无符号则跳 below)
CALL 调用子程序(函数),将返回地址压栈后跳转 CALL func(调用 func 函数)
RET 子程序返回,弹出栈顶返回地址跳转 RET(返回到 CALL 的下一条指令)
LOOP 循环指令(ECX 减 1,非零则跳转) LOOP again(ECX--,若≠0 则跳 again)

五、栈与帧操作指令

用于函数调用时的栈帧建立与销毁,32 位架构中尤为常用。

指令 功能说明 示例
ENTER 建立栈帧(替代 PUSH EBP; MOV EBP, ESP 等操作) ENTER 8, 0(分配 8 字节局部栈空间)
LEAVE 销毁栈帧(替代 MOV ESP, EBP; POP EBP) LEAVE(恢复 ESP 和 EBP)
PUSHFD/POPFD 压入 / 弹出 EFLAGS 寄存器(32 位) PUSHFD(保存标志位)

六、字符串操作指令

批量处理内存中的字符串 / 数组,需配合 ESI/EDI/ECX 等寄存器。

指令 功能说明 示例
MOVSB/MOVSW/MOVSD 字符串传送(ESI→EDI,每次 1/2/4 字节,DF 标志控制方向) REP MOVSD(重复 ECX 次,ESI→EDI 传送 4 字节)
CMPSB/CMPSD 字符串比较(ESI 与 EDI 指向数据比较) REPE CMPSB(相等则重复比较)
SCASB/SCASD 字符串扫描(AL/EAX 与 EDI 指向数据比较) REPNE SCASB(不等则重复扫描)
STOSB/STOSD 存储字符串(AL/EAX→EDI 指向内存) REP STOSD(重复 ECX 次,EAX 存入 EDI)

七、系统调用与特权指令

64 位 Linux/Windows 下的系统调用或特权级操作(如 x86-64 的SYSCALL)。

指令 功能说明 适用场景
INT 软中断(如 32 位 Linux 用INT 0x80触发系统调用,Windows 用INT 0x2E MOV EAX, 1; INT 0x80(Linux 32 位 exit 系统调用)
SYSCALL/SYSRET 64 位快速系统调用(Linux 用,Windows 用SYSCALL+SYSRET MOV RAX, 60; SYSCALL(Linux 64 位 exit)
IRET 中断返回(从异常 / 中断处理程序返回) 内核态中断处理

2. 选择语句的机器表示

选择语句(如if-elseswitch-case)的机器表示本质是通过条件判断指令和跳转指令改变程序执行流,不同选择结构对应不同的汇编实现逻辑,核心依赖处理器的标志寄存器(如 ZF、CF、SF)和跳转指令(如 JE、JMP)。以下是具体实现方式:

一、if-else语句的机器表示

if-else的核心是 "条件判断 + 分支跳转",编译器会将条件表达式转换为比较 / 测试指令,再根据标志位结果跳转到对应代码块。

复制代码
int a = 5, b = 3, result;
if (a > b) {
    result = 1;
} else {
    result = 0;
}
对应的 x86 汇编(AT&T 格式):
复制代码
movl    $5, %eax        # a=5 → EAX
movl    $3, %ebx        # b=3 → EBX
cmpl    %ebx, %eax      # 比较EAX(a)和EBX(b),设置标志位(a-b)
jle     .Lelse          # 若a ≤ b(JLE:小于等于则跳转),跳转到else块
# if块:a > b
movl    $1, %ecx        # result=1 → ECX
jmp     .Lend           # 跳过else块,直接到结束
.Lelse:
movl    $0, %ecx        # result=0 → ECX
.Lend:
# 后续代码
关键逻辑:
  1. 条件判断 :用CMP(比较)或TEST(测试)指令计算条件表达式,改变标志寄存器(如 ZF = 零标志、SF = 符号标志、CF = 进位标志)。
  2. 条件跳转 :根据标志位选择跳转指令(如JG(大于)、JLE(小于等于)、JE(等于)),跳转到对应代码块。
  3. 无条件跳转if块执行完后用JMP跳过else块,避免执行冗余代码。

3. 循环语句的机器表示

for循环本质是while循环的语法糖,汇编层面会拆解为 "初始化→条件检查→循环体→更新变量" 的结构,与while循环几乎一致。

示例代码(C 语言):
复制代码
int sum = 0;
for (int i = 0; i < 5; i++) {
    sum += i;
}
对应的 x86 汇编(AT&T 格式):
复制代码
movl    $0, %ebx        # sum=0 → EBX
movl    $0, %eax        # 初始化i=0 → EAX(for的初始化部分)
.Lloop_start:
cmpl    $5, %eax        # 条件检查:i < 5?
jge     .Lloop_end      # 不满足则退出循环
# 循环体:sum += i
addl    %eax, %ebx      # sum = sum + i
# 更新变量:i++
incl    %eax            # i = i + 1
jmp     .Lloop_start    # 回跳检查条件
.Lloop_end:
  1. 依赖跳转指令 :通过条件跳转(如JGEJL)控制循环是否继续,无条件跳转(JMP)实现回跳。
  2. 标志寄存器的作用 :条件跳转的依据是CMP/TEST指令设置的标志位(如 ZF、SF、CF)。
  3. 结构等价性for/while/do-while在机器码层面最终都会转化为 "条件检查 + 回跳" 的结构,仅执行顺序略有差异。
  4. 优化方向:编译器可能会对循环进行优化(如循环展开、寄存器重命名),减少跳转次数以提升性能。

总结

循环语句的机器级表示本质是 **"条件判断 + 回跳" 的组合 **:通过比较指令设置标志位,跳转指令控制执行流是否重复,不同循环语句(for/while/do-while)仅在条件检查和循环体的执行顺序上略有差异,最终都转化为高效的汇编跳转结构。

4. 函数调用的机器级表示

一、核心概念:栈帧(Stack Frame)

栈帧是函数调用时在栈上分配的独立内存区域,用于存储:

  • 函数参数、返回地址(调用者的下一条指令地址);
  • 函数的局部变量、寄存器上下文(如基址寄存器EBP/RBP);每个函数的栈帧由栈基址指针(EBP/RBP)栈指针(ESP/RSP) 界定。

二、x86 32 位架构的函数调用流程(CDECL 调用约定)

以经典的 32 位 x86 为例(C 语言默认调用约定),函数调用分为调用前准备→执行调用→函数执行→返回恢复四步。

示例代码(C 语言):
复制代码
// 调用者函数
int caller() {
    int a = 1, b = 2;
    int res = add(a, b); // 调用add函数
    return res;
}

// 被调用函数
int add(int x, int y) {
    int temp = x + y;
    return temp;
}
1. 调用前准备(caller 函数)
  • 参数压栈 :按 CDECL 约定,参数从右到左压栈(先y,再x);
  • 保存返回地址 :执行CALL指令时,自动将下一条指令地址压栈。

对应的汇编(AT&T 格式):

复制代码
caller:
    pushl   %ebp                ; 保存旧EBP(调用者的栈基址)
    movl    %esp, %ebp          ; 建立caller的栈帧(EBP=ESP)
    subl    $8, %esp            ; 分配局部变量空间(a和b,各4字节)
    movl    $1, -4(%ebp)        ; a=1 → EBP-4(局部变量)
    movl    $2, -8(%ebp)        ; b=2 → EBP-8(局部变量)
    
    ; 准备add函数的参数:从右到左压栈(先b,再a)
    pushl   -8(%ebp)            ; 参数y = b(2)压栈
    pushl   -4(%ebp)            ; 参数x = a(1)压栈
    
    call    add                 ; 调用add函数:自动将下一条指令地址压栈,跳转到add
2. 函数执行(add 函数)
  • 建立栈帧 :保存调用者的EBP,设置自身的EBP
  • 分配局部变量空间 :通过ESP偏移分配栈空间;
  • 执行函数逻辑 :计算x+y,结果存入返回寄存器EAX

对应的汇编:

复制代码
add:
    pushl   %ebp                ; 保存caller的EBP(栈顶现在是返回地址→caller的EBP)
    movl    %esp, %ebp          ; 建立add的栈帧(EBP=ESP)
    subl    $4, %esp            ; 分配局部变量temp的空间(4字节)
    
    movl    8(%ebp), %eax       ; 取参数x → EAX(EBP+8是第一个参数,EBP+4是返回地址)
    addl    12(%ebp), %eax      ; x + y(EBP+12是第二个参数y)→ EAX
    movl    %eax, -4(%ebp)      ; temp = x+y → EBP-4
    
    movl    -4(%ebp), %eax      ; 返回值存入EAX(约定用EAX传递返回值)
    
    leave                       ; 等价于movl %ebp, %esp + popl %ebp(销毁add的栈帧)
    ret                         ; 弹出返回地址,跳回caller的下一条指令
3. 返回恢复(caller 函数)
  • 清理栈参数:按 CDECL 约定,调用者负责清理压栈的参数;
  • 恢复栈帧:销毁自身栈帧,返回结果。

对应的汇编:

复制代码
    ; call add后的指令
    addl    $8, %esp            ; 清理add的参数(2个参数×4字节=8字节)
    movl    %eax, -12(%ebp)     ; res = add返回值(EAX)→ EBP-12
    movl    -12(%ebp), %eax     ; caller的返回值存入EAX
    
    leave                       ; 销毁caller的栈帧
    ret                         ; 返回caller的调用者

三、栈帧布局(关键!)

add函数执行时的栈为例,栈从高地址向低地址增长(ESP 指向栈顶),布局如下:

复制代码
高地址 →  [caller的EBP]        ; EBP(add的栈基址)指向这里
          [返回地址]           ; EBP+4:caller中call add的下一条指令地址
          [参数x (a=1)]        ; EBP+8:第一个参数(x)
          [参数y (b=2)]        ; EBP+12:第二个参数(y)
          [局部变量temp]       ; EBP-4:add的局部变量
低地址 →  [ESP]                ; 栈指针指向栈顶

四、x86 64 位架构的差异(System V AMD64 调用约定)

64 位架构不再完全依赖栈传递参数,而是用寄存器 + 栈结合:

  1. 参数传递 :前 6 个整数参数用寄存器RDI/RSI/RDX/RCX/R8/R9传递,超过 6 个的参数压栈;
  2. 返回值 :用RAX传递(64 位);
  3. 栈帧 :仍用RBP作为基址指针(可选,部分编译器优化后会省略),栈需 16 字节对齐。

示例(add 函数的 64 位汇编):

复制代码
add:
    pushq   %rbp
    movq    %rsp, %rbp
    addq    %rsi, %rdi          ; x(RDI) + y(RSI) → RDI
    movq    %rdi, %rax          ; 返回值存入RAX
    popq    %rbp
    ret

五、关键指令与约定

  1. CALL指令
    • 功能:① 将下一条指令地址压栈(返回地址);② 跳转到被调用函数入口;
  2. RET指令
    • 功能:① 弹出栈顶的返回地址;② 跳转到该地址(回到调用者);
  3. LEAVE指令
    • 等价于movl %ebp, %esp(恢复 ESP) + popl %ebp(恢复旧 EBP),快速销毁栈帧;
  4. 调用约定(Calling Convention)
    • 规定参数传递方式、栈清理责任、寄存器保存规则,常见的有:
      • CDECL :参数右到左压栈,调用者清理栈,支持可变参数(如printf);
      • STDCALL:参数右到左压栈,被调用者清理栈(Windows API 常用);
      • System V AMD64:64 位 Linux/macOS 的默认约定,寄存器 + 栈传参。

六、函数调用的核心逻辑总结

  1. 栈帧隔离:每个函数有独立栈帧,避免局部变量冲突;
  2. 参数传递:32 位用栈,64 位用寄存器 + 栈;
  3. 返回机制 :通过CALL/RET指令和返回地址实现执行流跳转;
  4. 寄存器约定 :部分寄存器(如EBP/ESI)需保存,部分(如EAX/EDX)可随意修改(由调用约定规定)。

本质上,函数调用的机器级实现是栈的 "push/pop" 操作 + 指令跳转的结合,通过严格的栈帧管理保证调用的正确性。

相关推荐
fengye2071611 天前
板凳————————————(枯藤 )vs2019+win10
汇编
T.Ree.1 天前
汇编_movsd和stosd
汇编
日更嵌入式的打工仔1 天前
MCUXpresso开启汇编调试
汇编·单片机·nxp·mcuxpresso
切糕师学AI2 天前
ARM中的汇编指令:MRS和MSR
汇编·嵌入式开发
缘友一世3 天前
计算系统安全速成之汇编基础【1】
汇编
white-persist4 天前
【攻防世界】reverse | IgniteMe 详细题解 WP
c语言·汇编·数据结构·c++·python·算法·网络安全
小邓   ༽4 天前
50道C++编程练习题及解答-C编程例题
c语言·汇编·c++·编程练习·c语言练习题
资料,小偿6 天前
4.1.2基于51单片机汇编语言出租车计价器proteus仿真出租车计价器,汇编语言51单片机
汇编·51单片机·proteus
ol木子李lo8 天前
Visual studio 2022高亮汇编(ASM)语法方法
汇编·ide·windows·visual studio