计算机组成原理(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" 操作 + 指令跳转的结合,通过严格的栈帧管理保证调用的正确性。

相关推荐
我在人间贩卖青春4 天前
汇编之伪指令
汇编·伪指令
我在人间贩卖青春4 天前
汇编之伪操作
汇编·伪操作
济6174 天前
FreeRTOS基础--堆栈概念与汇编指令实战解析
汇编·嵌入式·freertos
myloveasuka4 天前
汇编TEST指令
汇编
我在人间贩卖青春4 天前
汇编编程驱动LED
汇编·点亮led
我在人间贩卖青春4 天前
汇编和C编程相互调用
汇编·混合编程
myloveasuka5 天前
寻址方式笔记
汇编·笔记·计算机组成原理
请输入蚊子5 天前
《操作系统真象还原》 第六章 完善内核
linux·汇编·操作系统·bochs·操作系统真像还原
myloveasuka5 天前
指令格式举例
汇编·笔记·计算机组成原理
我在人间贩卖青春6 天前
汇编之分支跳转指令
汇编·arm·分支跳转