Day 5: 数据传送指令
本章介绍汇编语言中最基础的指令——数据传送指令。MOV是使用频率最高的指令之一。
1. MOV指令
1.1 基本用法
; MOV 目标, 源 ; 把源的值复制到目标 mov eax, 100 ; 寄存器 ← 立即数 mov eax, ebx ; 寄存器 ← 寄存器 mov eax, [var] ; 寄存器 ← 内存 mov [var], eax ; 内存 ← 寄存器 mov [var], 100 ; 内存 ← 立即数(需要指定大小) mov dword [var], 100 ; 不能直接 内存 ← 内存! ; mov [var1], [var2] ; 错误! ; 正确做法: mov eax, [var2] mov [var1], eax1.2 MOV的限制
MOV指令的规则: 1. 目标和源大小必须匹配 mov eax, bl ; 错误!32位←8位 movzx eax, bl ; 正确:零扩展 2. 不能内存到内存 mov [a], [b] ; 错误! 3. 段寄存器有特殊规则 mov cs, ax ; 错误!CS只读 mov ds, ax ; 正确 4. 不能把立即数直接送入段寄存器 mov ds, 0x10 ; 错误! mov ax, 0x10 mov ds, ax ; 正确2. 数据扩展指令
2.1 零扩展 MOVZX
; MOVZX - Move with Zero Extend ; 高位补0 mov bl, 0xFF ; BL = 255 movzx eax, bl ; EAX = 0x000000FF = 255 movzx ax, bl ; AX = 0x00FF ; 常用于无符号数扩展2.2 符号扩展 MOVSX
; MOVSX - Move with Sign Extend ; 高位补符号位 mov bl, 0xFF ; BL = -1 (有符号) movsx eax, bl ; EAX = 0xFFFFFFFF = -1 mov bl, 0x7F ; BL = 127 movsx eax, bl ; EAX = 0x0000007F = 127 ; 常用于有符号数扩展2.3 对比示例
mov bl, 0x80 ; 二进制:1000 0000 movzx eax, bl ; EAX = 0x00000080 = 128 (无符号) movsx eax, bl ; EAX = 0xFFFFFF80 = -128 (有符号) ; 同样的位模式,解释不同!3. 交换指令
3.1 XCHG
; XCHG - 交换两个操作数的值 xchg eax, ebx ; 交换EAX和EBX xchg ax, cx ; 交换AX和CX xchg [var], eax ; 交换内存和寄存器 ; 等价于: ; temp = eax ; eax = ebx ; ebx = temp ; 但XCHG是原子操作!3.2 BSWAP
; BSWAP - 字节序反转(仅32位/64位寄存器) mov eax, 0x12345678 bswap eax ; EAX = 0x78563412 ; 用于大小端转换4. LEA指令
4.1 基本用法
; LEA - Load Effective Address ; 计算地址,但不访问内存 lea eax, [ebx + 4] ; EAX = EBX + 4 lea eax, [ebx + ecx*4] ; EAX = EBX + ECX*4 ; 对比MOV: mov eax, [ebx + 4] ; EAX = 内存[EBX+4]的值 lea eax, [ebx + 4] ; EAX = EBX + 4 的地址值4.2 LEA的妙用
; 快速计算 lea eax, [ebx + ebx*2] ; EAX = EBX * 3 lea eax, [ebx*4] ; EAX = EBX * 4 lea eax, [ebx + ebx*4] ; EAX = EBX * 5 lea eax, [eax + eax*8] ; EAX = EAX * 9 ; 一条指令完成多个操作 lea eax, [ebx + ecx + 10] ; EAX = EBX + ECX + 10 ; 获取变量地址 lea eax, [myVar] ; 等价于 mov eax, offset myVar5. 栈操作指令
5.1 PUSH和POP
; PUSH - 压栈 push eax ; ESP -= 4, [ESP] = EAX push 100 ; 压入立即数 push dword [var] ; 压入内存值 ; POP - 出栈 pop eax ; EAX = [ESP], ESP += 4 pop dword [var] ; 弹出到内存 ; 栈的增长方向:高地址 → 低地址 ; PUSH时ESP减小,POP时ESP增大5.2 PUSHA/POPA
; 32位模式 pushad ; 压入所有通用寄存器 ; 顺序:EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI popad ; 弹出所有通用寄存器 ; 用于函数开头/结尾保存/恢复寄存器 ; x64模式下不可用,需要手动push/pop5.3 栈操作示例
PUSH EAX 前: PUSH EAX 后: ESP指向 -> [???] [???] [???] ESP -> [EAX] <- 新压入的值 [???] [???] 注意:栈向低地址增长,PUSH后ESP减小6. 条件传送指令
6.1 CMOVcc
; CMOVcc - Conditional Move ; 根据标志位条件移动,避免分支 cmp eax, ebx cmovg ecx, edx ; if (eax > ebx) ecx = edx cmovl ecx, edx ; if (eax < ebx) ecx = edx cmove ecx, edx ; if (eax == ebx) ecx = edx cmovne ecx, edx ; if (eax != ebx) ecx = edx ; 常用条件: ; cmove/cmovz - 相等/零 ; cmovne/cmovnz - 不等/非零 ; cmovg/cmovnle - 大于(有符号) ; cmovl/cmovnge - 小于(有符号) ; cmova/cmovnbe - 大于(无符号) ; cmovb/cmovnae - 小于(无符号)6.2 CMOVcc vs 分支
; 传统分支写法: cmp eax, ebx jle skip mov ecx, edx skip: ; 使用CMOVcc: cmp eax, ebx cmovg ecx, edx ; CMOVcc优点: ; - 避免分支预测失败 ; - 更短的代码 ; 缺点: ; - 即使条件不满足也会读取源操作数7. 练习题
练习1:数据传送(难度:★★☆☆☆)
把内存变量var1的值复制到var2。
练习2:LEA计算(难度:★★☆☆☆)
用一条LEA指令计算 EAX * 7。
练习3:交换变量(难度:★★☆☆☆)
不使用XCHG,用3条MOV指令交换EAX和EBX。
8. 小结
[MOV指令] 1. MOV 目标, 源 2. 不能内存到内存 3. 大小必须匹配 [扩展指令] 4. MOVZX - 零扩展 5. MOVSX - 符号扩展 [交换指令] 6. XCHG - 交换 7. BSWAP - 字节序反转 [LEA指令] 8. 计算地址不访问内存 9. 可用于快速乘法 [栈操作] 10. PUSH - 压栈(ESP减小) 11. POP - 出栈(ESP增大) [条件传送] 12. CMOVcc - 避免分支下一篇预告:Day 6 - 算术运算指令