计组与微机控制
7.3 数据传送指令
7.3 数据传送指令
具体指令:
一. 按操作数个数分类
按照汇编指令格式,可以分为三类:
双操作数指令
有两个操作数:
MOV dest, src ADD dest, src CMP dest, src
例如:MOV AX, BX
其中:dest = AX src = BX
单操作数指令
只有一个操作数:INC AX DEC BX PUSH AX POP AX MUL BL
有时候这个操作数既是源,也是目的。
比如:INC AX
意思是:AX = AX + 1
AX 既提供原值,又接收结果。
无操作数指令
指令表面上没有写操作数,但可能隐含使用某些寄存器或标志位。
例如:CLC STC NOP XLAT
比如 XLAT 表面没有操作数,但隐含用 BX 和 AL。
二、数据传送类指令
数据传送类指令的作用是:把数据从一个地方传到另一个地方
传送的数据可以是:变量内容 变量地址标志寄存器内容 I/O 端口数据
注意,数据传送类指令一般不影响标志位。
课本说除了:SAHF POPF
之外,其余数据传送类指令执行后一般不影响标志寄存器。
三、数据传送类指令总表
课本表 4.7 把它们分成几类:

通用数据传送:MOV、XCHG、XLAT、PUSH、POP地址传送:LEA、LDS、LES标志位传送:LAHF、SAHF、PUSHF、POPFI/O 数据传送:IN、OUT
四、MOV 指令
MOV 是最常用的数据传送指令。
格式:MOV dest, src
功能:dest ← src也就是把源操作数的内容送到目的操作数。
注意:源操作数不变 目的操作数被覆盖
例如:MOV AX, BX
执行后:AX = BXBX 不变
1. MOV 可以传字节,也可以传字
课本说,MOV 的两个操作数可以是字节,也可以是字。
但是二者必须等长。
合法:MOV AL, BL MOV AX, BX
不合法:MOV AX, BL MOV AL, BX
一个是 16 位,一个是 8 位,长度不一致。
MOV 的常见形式:MOV 的五种形式。
1. 立即数送寄存器
MOV reg, imm
例如:MOV AX, 0B123H MOV AL, 12H
意思是把立即数送入寄存器。(首位ABCDEF前面必须加0,要不会被认为是符号名)
注意立即数只能作为源操作数,不能作为目的操作数。
可以:MOV AX, 1234H 不可以:MOV 1234H, AX
2. 寄存器 / 段寄存器之间传送
MOV reg/sreg, reg MOV reg, sreg
例如:MOV DL, CL MOV DS, AX
这里要注意,段寄存器不能直接用立即数赋值。
不能写:MOV DS, 2000H
通常要通过通用寄存器中转:
MOV AX, 2000H MOV DS, AX
3. 立即数送内存
MOV mem, imm例如:MOV [BP + DI], 3210H
意思是把立即数 3210H 写到内存单元中。
如果内存操作数的大小不明确,要用 BYTE PTR 或 WORD PTR 指明。
比如:
MOV BYTE PTR [DI], 12H MOV WORD PTR [DI], 1234H
4. 寄存器 / 段寄存器送内存
MOV mem, reg/sreg
例如:MOV [BX + SI], AX表示把 AX 写入内存。
教材还写了类似:MOV [CX + 2], ES
但按 8086 寻址规则,CX 不能作为存储器寻址寄存器。真正合法的基址/变址寄存器是 BX、BP、SI、DI。有些老教材或印刷示例可能存在简化或不严谨,考试以老师给的规则为准。
5. 内存送寄存器 / 段寄存器
MOV reg/sreg, mem
例如:
MOV CX, BUFFER MOV DS, [BX]
意思是从内存取数据送到寄存器或段寄存器。
五、使用 MOV 的几个限制
课本列了很重要的规则。
1. 立即数只能作为源操作数
合法:MOV AX, 1234H 不合法:MOV 1234H, AX
因为立即数是常数,不能被写入。
2. CS 只能作为源操作数,不能作为目的操作数
也就是说可以读 CS:MOV AX, CS
但不能直接写:MOV CS, AX
因为 CS 控制当前代码段,如果随便 MOV 修改 CS,程序执行流会混乱。
要改变 CS,通常通过:
JMP FAR CALL FAR RET FAR INTIRET
这些控制转移指令实现。
3. 源操作数和目的操作数不能同时是内存
不能写:MOV [2000H], [1000H]
因为 8086 普通双操作数指令不允许内存到内存。
要通过寄存器中转:
MOV AX, [1000H] MOV [2000H], AX
4. 立即数不能直接送段寄存器
不能:MOV DS, 2000H
应该:MOV AX, 2000H MOV DS, AX
5. 不同段寄存器之间不能直接传送
比如不能:MOV DS, ES
通常也要通过通用寄存器:
MOV AX, ES MOV DS, AX
6. 源和目的长度必须一致
合法:MOV AX, BX MOV AL, BL
不合法:MOV AX, BL MOV AL, BX
如果内存操作数大小不明确,要写:
BYTE PTR WORD PTR DWORD PTR
例如:MOV BYTE PTR LABEL, BL MOV WORD PTR [DI], AX
六、例 4.3:把 DATA 的内容送 DS 和 ES
课本例子是:
MOV AX, DATA MOV DS, AX MOV ES, AX
思路是:不能直接 MOV DS, DATA也不能直接 MOV ES, DATA
所以先把 DATA 送到 AX,再由 AX 送到 DS 和 ES。
也就是说 AX 充当中间寄存器。
这个模式很常见:MOV AX, 数据段段地址MOV DS, AX
初始化 DS 时经常这样写。
七、XCHG 交换指令
XCHG oprd1, oprd2
功能是:oprd1 ↔ oprd2
也就是两个操作数内容互相交换。
例如:XCHG AX, BX
执行前:AX = 1111H BX = 2222H
执行后:AX = 2222H BX = 1111H
1. XCHG 不影响标志位
XCHG 只是交换数据,不做加减逻辑判断,所以不会影响 FLAGS。
2. XCHG 的合法形式
课本列:
XCHG reg, reg XCHG mem, reg XCHG reg, mem
例如:
XCHG AX, BX XCHG [ADDR], BX XCHG BX, [BP + SI]
但是不能:XCHG mem, mem 也不能:XCHG reg, imm
3. 两个内存单元之间怎么交换?
例 4.4 讲这个问题。
如果要交换两个内存单元 ADD1 和 ADD2 的内容,不能直接写:XCHG ADD1, ADD2
因为两个都是内存操作数。要用寄存器中转:
MOV AL, ADD1 XCHG AL, ADD2 XCHG AL, ADD1
我们假设:ADD1 = 11H ADD2 = 22H
第一条:MOV AL, ADD1
执行后:AL = 11H ADD1 = 11H ADD2 = 22H
第二条:XCHG AL, ADD2
执行后:AL = 22H ADD2 = 11H ADD1 = 11H
第三条:XCHG AL, ADD1
执行后:AL = 11H ADD1 = 22H ADD2 = 11H
最终 ADD1 和 ADD2 交换成功。
八、XLAT 查表转换指令
XLAT它是 Translate,查表转换指令。
格式表面上没有操作数:XLAT 但它隐含使用:BX 和 AL
功能是:AL ← [BX + AL]
更完整地说:AL ← DS:[BX + AL]
1. XLAT 的作用
XLAT 用来根据 AL 中的下标,到表中查一个字节,再把查到的值放回 AL。
可以把它理解成 C 语言里的:
AL = table[AL];
其中:
BX = table 的首地址AL = 要查的下标
执行后:
AL = 表中对应项的值BX 不变
2. 使用 XLAT 的步骤
一般流程是:
MOV BX, OFFSET TABLE MOV AL, 下标XLAT
执行后,AL 就变成查表结果。
3.例 4.5:BCD 码转 7 段 LED 显示码
课本说,数字 0~9 对应的七段显示码放在一张表中。
例如表中可能是:
数字 0 → 40H数字 1 → 79H数字 2 → 24H数字 3 → 30H数字 4 → 19H...
如果要查数字 4 对应的显示码:
MOV BX, OFFSET HEX_TABLE MOV AL, 4XLAT
执行过程:
BX = 表首地址AL = 4CPU 访问 DS:[BX + 4]把该单元内容送入 AL
如果表中第 4 项是 19H,那么执行后:AL = 19H
这就完成了从数字 4 到七段码 19H 的转换。
九、PUSH 和 POP 堆栈操作指令(老生常谈之前说过好多次了)
堆栈是先进后出结构:后放进去的,先取出来
8086 的堆栈以字为单位操作,也就是每次 PUSH/POP 都是 16 位,两个字节。
PUSH 指令格式:
PUSH src功能:把 16 位源操作数压入堆栈
课本给出的过程是:
SP ← SP - 2[SP + 1 : SP] ← src
更容易理解:先让 SP 减 2,再把一个字写入 SS:SP 指向的栈顶
为什么 SP 要减 2?因为 8086 堆栈向低地址方向增长,并且每次压入一个字,占 2 个字节。
1. PUSH 的小端存放(栈从下面往上生长,也就是一开始指针在栈底)
假设:AX = 1234H SS = 3000H SP = 1000H
执行:PUSH AX
第一步:
SP = SP - 2 = 0FFEH
第二步,把 AX 写入 SS:0FFEH 开始的两个字节:
SS:0FFEH = 34HSS:0FFFH = 12H
所以低字节 34H 放低地址,高字节 12H 放高地址。
执行后:SP = 0FFEH
二十六、POP 指令
格式:POP dest
功能:从栈顶弹出一个 16 位数据送到目的操作数
过程是:
dest ← [SP + 1 : SP]SP ← SP + 2
更容易理解:先从 SS:SP 取一个字,再让 SP 加 2
1. POP 例子
接着刚才的例子,栈顶有:
SS:0FFEH = 34HSS:0FFFH = 12HSP = 0FFEH
执行:POP BX
CPU 从 SS:0FFEH 和 SS:0FFFH 取一个字:BX = 1234H
然后:SP = SP + 2 = 1000H
这就完成弹栈。
PUSH/POP 的本质
可以这样记:PUSH:SP 先减 2,再写入数据POP:先读出数据,SP 再加 2
方向:堆栈向低地址增长(也就是默认栈底在高地址,也就是下面,栈顶在低地址,也就是上面) PUSH 让 SP 变小POP 让 SP 变大
这个在 CALL、RET、中断处理中非常重要。
因为调用子程序时,返回地址就是通过堆栈保存的。
总结:这几页最核心的是两块:
1. 寻址方式补全
寄存器相对寻址:EA = BX/BP/SI/DI + disp基址变址相对寻址:EA = BX/BP + SI/DI + dispI/O 直接端口寻址:端口号直接写在指令中,范围 00H~FFHI/O 间接端口寻址:端口号放在 DX 中,范围 0000H~FFFFH隐含寻址:操作数不写出来,但 CPU 默认使用某些寄存器
2. 数据传送指令开头
MOV:传送XCHG:交换XLAT:查表转换PUSH:压栈POP:出栈
特殊记忆:
MOV 不能内存到内存MOV 不能立即数直接送段寄存器CS 不能作为 MOV 的目的操作数XCHG 不能内存和内存直接交换XLAT 隐含 BX 和 AL,功能是 AL ← [BX + AL] PUSH 先 SP-2 再写栈POP 先读栈再 SP+2
这里开始,汇编味道就很重了。后面继续就是 PUSH/POP 的具体形式,以及 LEA、LDS、LES、LAHF、SAHF、PUSHF、POPF、IN、OUT 等指令
