Data Movement Instructions¶
约 2098 个字 161 行代码 预计阅读时间 13 分钟
This chapter concentrates on the data movement instructions. The data movement instructions include
MOV,MOVSX,MOVZX,PUSH,POP,BSWAP,XCHG,XLAT,IN,OUT,LEA,LDS,LES,LFS,LGS,LSS,LAHF,SAHF. String instructions:MOVS,LODS,STOS,INS, andOUTS. Data movement instructions do not affect flags.
编码格式¶
cm 老师说往年都没有考,今年要考(2025-2026 秋冬)
在 code segment descriptor 中,存在两个位指示了指令的长度模式:
- L=0 and D/B =0 for 16-bit instruction mode
- L=0 and D/B =1 for 32-bit instruction mode
- L=1 for 64-bit instruction mode
在不同运行模式下,同一指令的编码可能不一样。下面我们对比如下两种 format:
例如,对于如下指令,它在 80286(16-bit)和 80386(32-bit)下编码就有所不同:
x86 中,指令长度范围为 1 Byte - 15 Bytes。我们尝试分析指令编码中的几个关键字段:
- Opcode
- 如果 Direction Bit = 1,数据流从 R/M field 流向 REG field,即
MOV REG, R/M(Register or Memory) - 如果 Direction Bit = 0,数据流从 REG field 流向 R/M field,即
MOV R/M, REG
- 如果 Direction Bit = 1,数据流从 R/M field 流向 REG field,即
为什么不像 RISC-V 一样指定 rs 和 rd 呢?
这是因为 x86 中寻址不仅仅是寄存器寻址,还有内存寻址。所以 x86 选择固定寻址的两个域以及数据流的方向。
8A5501H,MOV DL, [DI+1],MOD 变了,加了 disp
32-bit 中的 Scaled-Index Byte 格式如下:
当 R/M 等于 100 时,Scaled-Index Byte 被使用:
64-bit 下,... TODO
Instruction Set¶
本节主要讲解相关数据操作指令。
LOAD EFFECTIVE ADDRESS¶
Load-effective address instruction 被设计用来支持 high-level language,其类型分为两种:
LEA: 加载 near point,即 offsetLDS,LES,LFS,LGS,LSS: 加载 far point,即 segment selector & offset
LEA
- 指令格式: lea dest, src
- 指令效果: 取变量的偏移地址
- 注意: 等效于
mov dx, offset abc但是可以用来作加法lea dx, ds:[bx+si+3]正确mov dx, bx+si+3错误lea eax, [eax+eax*4]利用lea做常数乘法- 指示符
OFFSET会在编译时替换为立即数,因此效率更高;但需要动态取址是用不了
LES
- 指令格式: les dest, src
- 指令效果: 将src指向地址的高十六位写入es,低十六位写入dest
- 注意: 由于采取小端存储法,所以是后面的存入es,例如:
- 从
ds:[bx]连续存放四个字节:9A,78,56,34 les di, ds:[bx]ES=3456h, DI=789Ah
- 从
LDS
- 指令格式: lds dest, src
- 指令效果: 将src指向地址的高十六位写入ds,低十六位写入dest
- 注意: 注意事项同上
Example
设有一内存字X,X的偏移地址与段地址按顺序存放在从地址 1000:10F0 起的内存单元中,请写出汇编指令把X的值赋值给寄存器AX。(要求使用LDS指令)
设有一内存字节Y,Y的偏移地址与段地址按顺序存放在从地址1000:10F0起的内存单元中,请写出汇编指令把Y的值加1。(要求使用LES指令)
最常使用的取远地址指令是 LSS,它可以用来更方便地切换栈:
STRING DATA TRANSFER¶
x86 指令只有两个操作数,因此不得不隐式使用各种默认寄存器
字符串操作指令包括:
movs字符串复制 move stringcmps字符串比较 compare stringscas字符串搜索 scan stringstos写入字符串 store stringlods读取字符串 load stringins从 io 接口读取字符串 in stringouts向 io 接口写入字符串 out string
与字符串操作指令相关的指令前缀包括:
rep重复 repeatrepe若相等则重复 repeat if equalrepz若结果为零则重复 repeat if zero ,与repe等效repne若不相等则重复 repeat if not equalrepnz若不为零则重复 repeat if not zero , 与repne等效
字符串操作指令可以与指令前缀结合使用,也可以单独使用。单独使用表示仅执行一次字符串操作,而加了指令前缀则可以最多重复执行 CX 次。
指令后缀 B 则表示操作数的大小。
REP MOVSB
- 指令格式: rep movsb
- 指令效果: 以字节为单位从
DS:[SI]复制字符串到ES:[DI]- byte ptr ES:[DI] = byte ptr DS:[SI];
- if(DF == 0) {SI++; DI++} 即正向复制
- if(DF == 1) {SI--; DI--} 即反向复制
- CX--; if(CX == 0) goto DONE;
REPE CMPSB
- 指令格式: repe cmpsb
- 指令效果: 以字节为单位比较
DS:[SI]与ES:[DI]的字符串,并将 SI 和 DI 停留在第一次不相等的地方- cmp byte ptr ES:[DI], byte ptr DS:[SI];
- if(DF == 0) {SI++; DI++} 即正向比较
- if(DF == 1) {SI--; DI--} 即反向比较
- CX--; if(CX == 0 || ZF != 0) goto DONE;
- 注意:
CMPS指令不影响标志位,当退出该指令时,标志寄存器 FL 恢复;但是仍然可以用je等指令实现判断跳转
可以自己试试下面的程序的执行效果:
$
这里数据段中有一句 slen = $ - offset s,作用是计算数组 s 的长度。但是 slen 本身不会出现在数据段内,而是类似于宏的存在。
REPE SCASB
- 指令格式: repe scasb
- 指令效果: 在
ES:[DI]指向的目标字符串中搜索 AL|AX|EAX 的值 - 注意: 基本原理基本同上,一般只利用 CX 前后的变化求字符串长度
用 repne scasb 求字符串长度:
REP STOSB
- 指令格式: rep stosb
- 指令效果: 把 AL|AX|EAX 的值写入
ES:[DI]指向的目标字符串 - 注意: 基本原理基本同上
REP LODSB
- 指令格式: rep lodsb
- 指令效果: 从
ES:[DI]中读取 x 个字节保存在 AL|AX|EAX 中 - 注意: 基本原理基本同上,x 为对应的字节长度
INS / OUTS
- 指令格式:
- INSB / INSW / INSD
- OUTSB / OUTSW / OUTSD
- 指令效果:
这两条都是串操作指令,用于在端口与内存之间进行数据块传输,配合REP、REPE、REPNE等前缀可实现批量输入输出。INS:从 I/O 端口读入数据 → 写入到内存地址ES:[DI]OUTS:从内存地址DS:[SI]读数据 → 写入到 I/O 端口- 若方向标志
DF = 0:SI、DI递增(正向传输) - 若方向标志
DF = 1:SI、DI递减(反向传输) - 执行完一次后,若带有
REP前缀,则会自动CX--并循环直到CX=0
- 注意事项:
-
INS默认目的段为 ES,而源为 I/O 端口,因此不能更改目的段寄存器。
-
OUTS默认源段为 DS,而目的为 I/O 端口,同样不能修改目标段。
-
- 两者都使用 DX 指定端口号,必须保证端口已被正确映射或允许访问。
-
- 一般在系统级编程、设备驱动或操作系统中使用,用户态程序通常不可直接使用。
-
- 当使用
REP INS或REP OUTS时,CPU 会自动根据CX的值进行批量 I/O 操作。
- 当使用
-
MISCELLANEOUS DATA TRNASFER¶
最后是一些不好分类的数据传输指令,包括 XCHG, LAHF, SAHF, XLAT, IN, OUT, BSWAP, MOVSX, MOVZX, CMOV 等。
XCHG
- 指令格式: xchg op1, op2
- 指令效果: 交换 op1 和 op2
- 注意:
xchg不影响任何标志位;且不能对段寄存器操作
XHCG 可以用来在进程同步之间实现自旋锁,例如:
LAHF
- 指令格式: lahf
- 指令效果: 将 EFLAGS 的低八位放入 AH
- 注意: AH := (SF:ZF:0:AF:0:PF:1:CF)
SAHF
- 指令格式: sahf
- 指令效果: 从 AH 读入 EFLAGS 的低八位
XLAT
- 指令格式: xlat
- 指令效果: al = byte ptr ds:[bx+al]
- 注意: 相当于 bx 是基地址,al 是偏移地址,用来查表操作
利用 xlat 指令和转换表将十进制整数转换成十六进制并输出:
MOVSX
- 指令格式: movsx dest, src
- 指令效果: 把值 src 符号扩充至 dest 中
- 注意: dest 的位宽需大于 src
- 例子:
movsx bx, al ; al = 80h=>bx = 0FF80h
MOVZX
- 指令格式: movzx dest, src
- 指令效果: 把值 src 零扩充至 dest 中
- 注意: dest 的位宽需大于 src
- 例子:
movzx bx, al ; al = 80h=>bx = 0080h
BSWAP
- 指令格式: bswap reg
- 指令效果: 逆向指定寄存器的 byte order,相当于大端小端格式转换
- 注意: 其 opcode 格式较特殊
[REX.W, 0F, C8 + rd]
CMOVcc
- 指令格式: cmovcc dest, src
- 指令效果: 如果满足条件
cc,则执行 mov - 注意: cc 可以是
AE,BE,Z,G,E等等,与控制转移指令类似




