现在位置: 首页 > 汇编语言 > 正文

汇编语言 - 寄存器

寄存器(Register)是 CPU 内部的高速存储单元,是汇编编程中最频繁操作的对象。理解寄存器是学好汇编语言的关键第一步。


什么是寄存器

寄存器是 CPU 芯片内部集成的 超高速小型存储器,用于暂存指令、数据和地址。

与内存不同,寄存器就嵌在 CPU 内部,CPU 访问寄存器几乎零延迟,而访问内存则需要几十到几百个时钟周期。

在汇编语言中,绝大多数运算都是围绕寄存器展开的——数据从内存加载到寄存器,在寄存器中完成运算,再将结果存回内存。

可以把寄存器理解为 CPU 的"工作台"。工作台上的工具随时可用,而内存则像是仓库,需要走过去取放。


x86 32 位寄存器分类

x86 32 位架构提供了多种类型的寄存器,各有不同的用途:


通用寄存器

通用寄存器(General Purpose Registers) 是最常用的寄存器,用于存放运算数据和临时结果。

x86 提供了 8 个 32 位通用寄存器:

32位16位低8位高8位(低16位)主要用途
EAXAXALAH累加器,存放函数返回值、算术运算结果
EBXBXBLBH基址寄存器,常用于存放内存基地址
ECXCXCLCH计数器,常用于循环计数和移位
EDXDXDLDH数据寄存器,存放乘除法的高位结果
ESISISIL-源变址寄存器,字符串操作的源地址
EDIDIDIL-目的变址寄存器,字符串操作的目标地址
EBPBPBPL-基址指针,指向当前栈帧的底部
ESPSPSPL-栈指针,始终指向栈顶

名称规律:E 前缀表示 Extended(扩展到 32 位),X 后缀表示可拆分为高低字节。

实例

; 文件路径:register_parts.asm
; 演示寄存器的各部分访问

section .text
    global _start

_start:
    mov eax, 0x12345678     ; 完整的 32 位寄存器
    ; 此时:EAX = 0x12345678
    ;       AX  = 0x5678      (低 16 位)
    ;       AH  = 0x56        (高 8 位,指 AX 的高 8 位)
    ;       AL  = 0x78        (低 8 位)

    mov ax, 0xAABB          ; 修改 AX(低 16 位)
    ; 此时:EAX = 0x1234AABB  (高 16 位保持不变!)
    ;       AX  = 0xAABB
    ;       AL  = 0xBB

    mov al, 0xCC            ; 修改 AL(最低 8 位)
    ; 此时:EAX = 0x1234AACC  (只有低 8 位变了)
    ;       AX  = 0xAACC
    ;       AL  = 0xCC

    mov eax, 1
    mov ebx, 0
    int 0x80

修改 32 位寄存器的低 16 位(如 AX)时,高 16 位保持不变。但将 32 位寄存器作为目标操作数时,会覆盖整个 32 位。这是初学者容易出错的地方。


段寄存器

段寄存器用于指定当前使用的内存段:

寄存器名称用途
CS代码段寄存器指向当前指令所在的段
DS数据段寄存器指向数据所在的段
SS栈段寄存器指向栈所在的段
ES附加段寄存器额外的数据段
FS附加段寄存器通用,常用于线程局部存储
GS附加段寄存器通用,常用于线程局部存储

在 32 位保护模式下编程时,操作系统已经设置好了段寄存器,你通常不需要手动修改它们。


指针和变址寄存器

这些寄存器主要用于访问内存,存放内存地址:

寄存器全称用途
EIP指令指针(Instruction Pointer)指向 CPU 下一条要执行的指令地址(不可直接访问)
ESP栈指针(Stack Pointer)指向栈顶,PUSH/POP 指令自动调整它
EBP基址指针(Base Pointer)指向当前函数栈帧的底部,用于访问函数参数和局部变量
ESI源变址(Source Index)字符串/内存操作的源地址
EDI目的变址(Destination Index)字符串/内存操作的目标地址

ESP 和 EBP 不能当作普通通用寄存器随意使用。ESP 指向栈顶,push/pop/call/ret 都会改变它;EBP 是访问函数参数的关键。随意修改它们会导致程序崩溃。


标志寄存器(EFLAGS)

EFLAGS 是一个 32 位寄存器,每个比特位代表一个状态标志(Flag)。

你不能直接读写整个 EFLAGS,但 CPU 会根据运算结果自动更新这些标志位,条件跳转指令则根据标志位决定是否跳转。

标志位名称含义
CF进位标志(Carry Flag)无符号运算产生进位/借位时置 1
PF奇偶标志(Parity Flag)结果低 8 位中 1 的个数为偶数时置 1
AF辅助进位标志低 4 位向高 4 位进位/借位时置 1
ZF零标志(Zero Flag)运算结果为 0 时置 1
SF符号标志(Sign Flag)运算结果为负数时置 1(等于结果的最高位)
OF溢出标志(Overflow Flag)有符号运算溢出时置 1

实例

; 文件路径:flags_demo.asm
; 演示运算对标志位的影响

section .text
    global _start

_start:
    mov eax, 10
    sub eax, 10         ; 10 - 10 = 0
    ; ZF = 1(结果为零)
    ; SF = 0(结果非负)
    ; CF = 0(无借位)

    mov eax, 0xFFFFFFFF
    add eax, 1          ; 0xFFFFFFFF + 1 = 0x100000000(超出了 32 位)
    ; ZF = 1(32位结果为0)
    ; CF = 1(产生进位)
    ; OF = 0(有符号角度看无溢出)

    mov eax, 1
    mov ebx, 0
    int 0x80

寄存器使用约定

在实际编程中,一些寄存器有约定俗成的用法——称为 调用约定(Calling Convention)

寄存器调用约定中的用途
EAX存放函数返回值
ECX计数器(循环计数)
EDX存放除法的高位结果、扩展 EAX
EBX、ESI、EDI、EBP被调用函数必须保存和恢复(callee-saved)
EAX、ECX、EDX调用者负责保存(caller-saved)

在编写自己的汇编程序时,不一定严格遵循调用约定。但如果你要和 C 语言混合编程,就必须遵守 cdecl 等调用约定。