学习指令关注:
- 指令的汇编格式
- 指令基本功能
- 指令支持的寻址方式
- 指令执行对标志位的影响
- 指令的特殊要求
数据传送类指令
- 通用数据传送指令:MOV、XCHG、PUSH、POP
- 累加器专用传送指令:IN、OUT、XLAX
- 地址传送指令:LEA、LDS、LES
- 标志寄存器传送指令:LAHF、SAHF、PUSHF、POPF
- 符号扩展指令:CBW、CWD
除了SAHF和POPF指令外,均不影响标志位
通用数据传送指令MOV
格式:MOV DST,SRC;
(DST)<--(SRC)
数据传输规则:
非法指令主要现象:
- 两个操作数的类型不一致,或者不确定;
MOV [BX][SI],1;
两个操作数类型不明确MOV BP,AL;
两操作数类型不一样
- CS不能作为目的操作数(不能显示地修改),DX不能作为存储器寻址的寄存器
- 传送方向错误
后续的双操作数指令一般也是这样规定指令的
数据交换指令XCHG(exchange)
格式:XCHG OPR1,OPR2;
(OPR1)<-->(OPR2) 两操作数值交换
注意:
- 遵守数据传输规则
- 两个操作数都不能使用段寄存器和IP,也不能使用立即数
换码指令XLAT(查表指令)
格式:XLAT
或XLAT OPR;
(DS:[BX+AL])->AL (OPR是为了提高可读性)
可用于将一种代码转换为另外一种代码
使用方法:
- 先在数据段中建立一张字节表格(长度不超过256,即2^8),表格的内容是要换取的代码
- 表格的首地址送寄存器BX
- 要换取的对应编码相对于表格首地址的位移量送寄存器AL
- 执行XLAT,AL中即为转换后的编码
堆栈操作
堆栈是主存中一块连续的存储区域,常用于数据的暂存、交换、子程序的参数传递等场合。
堆栈操作按照后进先出原则。
- 在8086系统里,堆栈所在的段为堆栈段,最大空间为64KB。
- 段地址由SS寄存器指示
- SP始终指示栈顶单元
- 8086系统从较大地址开始分配和使用(其他段地址从小地址开始)
SSEG SEGMENT STACK;堆栈段开始
DW 100 DUP(?) ;大小为100字
SSEG ENDS ;堆栈段结束
程序装入时,操作系统将SSEG的段基址置入SS,堆栈段的字节置入SP,即200(0C8H)
如果未定义堆栈段,用户程序装入内存时:
ES,DS指向PSP(Program Segment Prefix 程序段前缀区);SS指向用户程序区;CS指向用户代码段;(SP)=0000H指向64KB存储器尾部
对于8086系统堆栈操作:
-
进栈操作:PUSH SRC
- 执行:(SP) <- (SP)-2; ((SP)+1,(SP)) <- (SRC);
-
出栈出栈:POP DST
- 执行:(DST) <- ((SP)+1,(SP)); (SP) <- (SP)+2;
注意:
- 对于8086系统堆栈操作只支持字为单位的操作
- 不支持立即寻址
- CS不能作为目的操作数
地址传送指令
-
有效地址送寄存器指令:
LEZ REG, SRC; (REG) <- SRC
-
指针送寄存器和DS指令
LDS REG,SRC; (REG) <-(SRC), (DS) <- (SRC+2)
取两个字分别送往寄存器和DS
-
指针送寄存器和ES指令
LES REG, SRC; (REG) <- (SRC), (ES) <- (SRC+2)
注意:
- REG不能为段寄存器
- SRC必须为存储器寻址方式
标志寄存器传送指令
操作数都隐藏
-
标志送AH指令:LAHF ; (AH) <- (FLAGS低字节)
-
AH送标志寄存器指令:SAHF; (FLAGS低字节) <- (AH)
-
标志进栈指令:PUSHF; (SP) <- (SP)-2, ((SP)+1, (SP)) <- (FLAGS)
-
标志出栈指令:POPF; (FLAGS)<- ((SP)+1,(SP)) , (SP)<-(SP)+2
POPF,PUSHF可用于子程序中,分别用来恢复主程序标志与保存主程序标志
类型转换指令(符号位扩展指令)
- CBW; (AL)->AX 将字节扩展为字
- CWD; (AX)->(DX,AX) 将字扩展为双字
操作数隐含,扩展为符号位扩展。
若(AX) = BA45H
CWD; (DX)=FFFFH,(AX)=BA45H
CBW; (AX)=0045H
关于无符号扩展,只要将高半部分清0即可,可以通过MOV来实现
算法运算类指令
加法指令
- 加法指令:ADD DST,SRC ;(DST)<-(DST)+(SRC)
- 带进位加法:ADC DST,SRC ;(DST)<-(DST)+(SRC)+CF
- 加1指令:INC OPR
注意:
- 加法指令影响状态标志位,但INC指令不影响CF标志位
- 支持的操作数类型和MOV同,但操作数不能为段寄存器
减法指令
- 减法指令:SUB DST,SRC ;(DST)<-(DST)-(SRC)
- 带借位减法:SBB DST,SRC ;(DST)<-(DST)-(SRC)-CF
- 减一指令:DEC OPR ;(OPR)<-(OPR)-1
- 求补指令:NEG OPR ;(OPR)<-0-(OPR)
- 比较指令:CMP OPR1,OPR2 ;(OPR1)-(OPR2)
注意:
- 减法指令影响状态标志位,但DEC指令不影响CF标志位
- 减法指令对标志位的影响和加法指令不同。详细看
XYZW均为已定义的双字数据,用指令序列实现:
W<-X+Y+24-Z
DATA SEGMENT X DD 778899AAH Y DD 9ABCDEF0H Z DD 56789ABCH W DD ? DATA ENDS CODE SEGMRNTma ASSUME CS:CODE,DS:DATA START:MOV AX,DATA MOV DS,AX MOV AX,WORD PTR X MOV DX,WORD PTR X+2 ADD AX,WORD PTR Y ;X+Y低字部分 ADC DX,WORD PTR Y+2;X+Y高字部分 ADD AX,24 ADC DX,0 SUB AX,WORD PTR Z SBB DX,WORD PTR Z+2 MOV WORD PTR W,AX MOV WORD PTR W+2,DX ;结构存入W
乘法指令
- 无符号数乘法指令:MUL SRC
- 有符号数乘法指令:IMUL SRC
执行操作:
(根据SRC类型判断执行哪种操作,结果为双倍字长)
- 字节操作:(AX)<-(AL)X(SRC)
- 字操作:(DX,AX)<-(AX)x(SRC)
注意:
-
SRC若为存储器操作数时,注意ptr明确类型;SRC不能为立即数和段寄存器
-
除了CF与OF外,对其他标志位没有定义(执行后标志是任意的,不可预测)
-
乘法指令对CF/OF的影响:(高半位有无有效数字)
MUL指令:\(CF,OF=\left\{\begin{array}{rcl}00 &乘积的高一半为0\\11&否则\end{array}\right.\)
IMUL指令:\(CF,OF=\left\{\begin{array}{rcl}00 &乘积的高一半是低一半的符号扩展\\11&否则\end{array}\right.\)
除法运算
- 无符号数除法运算:DIV SRC
- 有符号数除法运算:IDIV SRC
执行操作:(被除数是除数的双倍)
-
字节操作:
(AL)<-(AX)/(SRC)的商
(AH)<-(AX)/(SRC)的余数
-
字操作
(AX)<-(DX,AX)/(SRC)的商
(DX)<-(DX,AX)/(SRC)的余数
注意:
- SRC若为存储器操作数时,注意ptr明确类型;SRC不能为立即数
- 对所有的状态标志位均无定义
- 两N位数相除,应首先把被除数符号扩展为2N位
位运算类指令
逻辑运算指令
- 非:NOT OPR
- 与:AND DST,SRC
- 或:OR DST,SRC
- 异或:XOR DST,SRC
- 测试:TEST OPR1,OPR2 ;(OPR1)与(OPR2)
注意:
- NOT指令不影响标志位;除了NOT外,其他均影响
- CF,OF标志位清0;SF,PF,ZF根据运算结果的特征设置;AF无定义
应用:
-
OR:有选择地使操作数置1
-
AND:有选择地清0
-
TEST:位测试,与条件转移指令一起完成对特定位的判断,实现相应的程序转移
判断偶数:
TEST AL,1 ;若为0则为偶数,则ZF=1 JZ EVEN
-
XOR:
- 有选择地取反(和1异或取反,和0异或不变)
- 寄存器清0(与自身进行逻辑异或,且执行速度快)
移位指令
-
逻辑左移:SHL OPR,CNT
CF<-左 右<-0
-
逻辑右移:SHR OPR,CNT
0->左 右->CF
-
算术左移:SAL OPR,CNT
CF<-左 右<-0
-
算术右移:SAR OPR,CNT
左->左 右->CF
-
循环左移:ROL OPR,CNT
CF<-左 右<-左
-
循环右移:ROR OPR,CNT
右->左 右->CF
-
带进位循环左移:RCL OPR,CNT
CF<-左 右<-CF(初始)
-
带进位循环右移:RCR OPR,CNT
CF(初始)->左 右->CF
操作数:
- OPR可用除立即数以外的任何寻址方式
- \(\left\{\begin{array}{rcl}CNT=1,&SHL&OPR,1\\CNT>1,&MOV&CL,CNT\\&SHL&OPR,CL\end{array}\right.\)
状态标志位:
-
CF=移除的值
-
\(OF=\left\{\begin{array}{rcl}1&CNT=1时,最高有效位的值发生变化\\0&CNT=1时,最高有效位的值不变\end{array} \right.\)
当CNT>1时,OF不确定
-
除此之外,移位指令SF、ZF、PF根据移位结果设置,AF无定义
-
循环移位指令:不影响SF、ZF、PF、AF
用移位指令实现:AX<-(AX)x10
SHL AX,1 MOV BX,AX SHL AX,1 ;AX<-4(原AX) SHL AX,1 ;AX<-8(原AX) ADD AX,BX ;AX<-(8AX)+(2AX)
将DX,AX中32位数值左移一维
SHL AX,1 RCL DX,1
表达式计算(应用)
X、Y、Z、V、W均为16位有符号数,计算W<-(V-(X*Y+Z-1234))/X
MOV AX,X IMUL Y ;X*Y MOV CX,AX MOV BX,DX ;(BX,CX)为X*Y MOV AX,Z CWD ;(DX,AX)为Z ADD CX,AX ADC BX,DX ;(BX,CX)为X*Y+Z SUB CX,1234 SBB BX,0 ;(BX,CX)为X*Y+Z-1234 MOV AX,V CWD ;(DX,AX)为V SUB AX,CX SBB DX,BX ;(DX,AX)为V-(X*Y+Z-1234) IDIV X ;(V-(X*Y+Z-1234))/X,商在AX,余数在DX MOV W,AX ;保存结果
转移控制类指令
指令寻址方式(确定下一条将要执行指令地址的方法)
-
顺序寻址
-
跳转寻址:通过转移控制类指令实现(主要是改变CS:IP的值)
如何确定转移控制类指令的转向地址?
-
直接寻址(用标号表达)
指令代码中直接给出地址差(目标地址相对于当前ip的位移量),将要转移到的目标地址就是当前IP值加上地址差。
-
间接寻址方式(用存储器或存储器操作数表达)
代码中指示寄存器或存储单元,目标地址从中获得
-
根据跳转范围不同,采用不同的转移方式:
-
段内寻址
-
近转移(near)
-
在当前代码64kB范围内注意
-
不需要修改CS段地址,只要改变IP偏移地址
-
-
短转移(short)
- 转移范围可以用一个字
-
-
段间寻址
远转移(far)
- 在1MB范围内
- 需要更改CS段地址和IP偏移地址
- 目标地址必须用一个32位数表达,即逻辑地址
实际编程过程中,汇编程序会选择处理转移方式,也可以人为规定。
无条件转移指令
JMP OPR; 程序无条件转向OPR指令的目标地址开始执行指令
JMP 指令支持段内、段间转移:
-
段内转移
JMP 标号
-
IP<-(IP)+标号(IP值的偏移量)
-
(CS)不变
-
-
段间转移
JMP FAR PTR 标号
- (IP)<-标号转移地址
- (CS)<-标号段地址
条件转移指令
有二十多个,将Jcc当做条件转移指令的一个统称:
JCC label;条件满足,发生转移
-
若转移:IP<-IP+8位位移量;否则顺序执行
-
只支持短转移
-
不影响标志位,但要利用标志位
根据利用的标志位不同,分为4种情况:
-
根据单个条件标志的设置情况转移
测试条件 JZ(JE) ZF=1 JNZ(JNE) ZF=0 JS SF=1 JNS SF=0 JO OF=1 JNO OF=0 JP PF=1 JNP PF=0 JC CF=1 JNC CF=0 -
测试CX的值为0则转移
JCXZ OPR ;(CX)=0
-
比较两个无符号数,根据比较结果转移
指令 转移条件 说明 JA/JNBE OPR CF=0且ZF=0 X>Y时,转移 JAE/JNB OPR CF=0或ZF=1 X>=Y时,转移 JNAE/JBOPR CF=1且ZF=0 X<Y时,转移 JNA/JBE OPR CF=1或ZF=1 X<=Y时,转移 -
比较两个有符号数,根据比较结果转移
指令 转移条件 说明 JG/JNLE OPR SF=OF且ZF=0 X>Y时,转移 JGE/JNL OPR SF=OF或ZF=1 X>=Y时,转移 JNGE/JL OPR SF!=OF且ZF=0 X<Y时,转移 JNG/JLE OPR SF!=OF或ZF=1 X<=Y时,转移
循环类指令
-
循环指令:
LOOP OPR
;测试条件:(CX)!=0 -
为0(相等)循环指令:
LOOPZ(LOOPE) OPR
;测试条件:ZF=1且(CX)!=0 -
不为零(不相等)循环指令:
LOOPNZ/LOOPNE OPR
;测试条件:ZF=0且(CX)!=0
执行步骤:
(CX)<-(CX)-1
- 检测是否满足测试条件,如果满足则
(IP)<-(IP)+8
位位移量,循环;不满足则退出循环,顺序执行。
注意:
-
CX
为隐含计数器,存放循环次数 -
只支持短转移
-
使用LOOPZ/LOOPE,LOONZ/LOOPNE指令来控制循环时,既有计数(CX)控制又有条件(ZF)控制。有两种可能会结束循环。所以一般应用在循环结束后用条件转移指令分开这两种情况,分别处理。
MOV CX,10
L1: ...
...
LOOP L1 ;L1到LOOP指令之间指令序列将重复执行10次
指令"MOV CX,10"称为"装载循环计数器",必须在循环前执行;若在循环中执行会造成死循环
计算1+2+3……+100,结果保存到SUM中
XOR AX,AX;累加器清0 MOV BX,1 ;BX<-1 MOV CX,100 AGAIN:ADD AX,BX INC BX LOOP AGAIN MOV SUM,AX
子程序调用和返回指令
子程序是完成特定功能的一段程序
当主程序执行这个功能时,采用CALL调用指令转移到子程序起始处;完成子程序后,采用RET指令返回到主程序继续执行(利用堆栈保存返回地址)
子程序定义伪指令:
子程序名 PROC 属性
... ;子程序体
子程序名 ENDP
- 子程序名:子程序入口的序号地址,子程序名应为合法的标识符,子程序名不能与同一源程序中的标号、变量名、其他子程序名等相同
- 属性:类型属性(NEAR(缺省),FAR)
-
段内调用与返回
-
段内直接调用
CALL 子程序名
;NEAR属性操作:
- (SP)<-(SP)-2
- (SS:SP)<-(IP)
- (IP)<-子程序入口偏移地址
-
段内返回
RET
;按NEAR属性返回操作:
- (IP)<-(SS:SP) ;从堆栈中取出返回地址
- (SP)<-(SP)+2
-
带参数返回
RET n
;带参数返回操作:
- (IP)<-(SS:SP) ;从堆栈中取出返回地址
- (SP)<-(SP)+2+n;调整栈顶位置,回到之前的值
-
-
段间调用与返回
-
段间调用
CALL 子程序名
操作:
- (SP)<-(SP)-2
- (SS:SP)<-(CS) ;入栈
- (SP)<-(SP)-2
- (SS:SP)<-(IP) ;入栈
- (IP)<-子程序入口的偏移地址
- (CS)<-子程序入口的段地址
-
段间返回
RET
;按FAR属性返回- (IP)<-(SS:SP)
- (SP)<-(SP)+2
- (CS)<-(SS:SP)
- (SP)<-(SP)+2
-
ZEROBYTES PROC
PUSH AX
PUSH CX ;保护现场
XOR AX,AX ;AX清0
MOV CX,128 ;循环次数CX
ZEROLOOP: MOV [BX],AX ;将BX所对应的地址单元清0
ADD BX,2 ;修改地址
LOOP ZEROLOOP
POP CX ;恢复现场
POP AX
ZEROBYTES ENDP
中断与中断返回指令
中断指令有三条:INT、IRET、INTO
8086CPU支持256个中断,每个中断用一个8位二进制编码标识(即中断类型码,也称中断向量号)
中断向量:中断服务程序的入口地址(首地址),为含有段地址CS和偏移地址IP的32位逻辑地址。
中断向量与中断向量号的区别?
与子程序不同,中断指令程序不能用中断程序名标识中断地址,因为中断有随机性。
中断指令是统一管理的。
8086系统从物理地址0000H开始集中存放个中断向量。
- 按照中断类型码从小到大的顺序依次存放
- 每个中断向量占4个字节(32位),采用小端方式存储,其中高字为段地址,低字为偏移地址
- 256个中断占1KB区域(256 * 4=210 字节)即中断向量表
- 类型码为N的中断向量存放的物理地址为4N
中断指令:INT n
执行操作:
(SP)<-(SP)-2
((SP)+1,(SP))<-(FLAGS)
(SP)<-(SP)-2
((SP)+1,(SP))<-(CS)
(SP)<-(SP)-2
((SP)+1,(SP))<-(IP)
(IP)<-(n*4)
(CS)<-(n*4+2)
中断返回指令:IRET
执行操作:
(IP)<-((SP)+1,(SP))
(SP)<-(SP)+2
(CS)<-((SP)+1,(SP))
(SP)<-(SP)+2
(FLAGS)<-((SP)+1,(SP))
(SP)<-(SP)+2
注意:
- n(0~255)是中断类型。由于类型码为N的中断向量存放的物理地址为4N,因而能找到它在中断向量表对应的中断向量,即段地址:偏移地址
- INT会清零IF和TF,但不影响其他标准位
- IRET影响标志位由堆栈中取出的值确定
一个指令:INT proc ;proc为23H
如何手动存储CS:IP值?
XOR AX,AX ;AX清零 MOV DS,AX MOV SI,34H*4 ;DS:SI = 0:34H*4 该中断类型的逻辑地址 MOV WORD PTR [SI],offset proc MOV WORD PTR [SI+2],SEG
溢出中断指令INTO
功能: 检测OF标志位
- OF=1时产生中断类型为4的中断
- OF=0时不起作用
指令完成操作(中断类型码为4):
- 标志寄存器入栈
- 断点地址入栈,CS先入,然后IP入
- 从中断向量表中获取中断服务程序入口地址(
IP<-(0:11H,0:10H) CS<-(0:12H,0:13H)
)
处理机控制指令
用于修改标志寄存器的标志位或控制CPU的动作
- 标志位操作指令完成标志位的复位,置位等操作
- 外部同步指令用于控制CPU,不影响标志位
标志位操作指令格式 | 操作 |
---|---|
STC | CF<-1 |
CLC | CF<-0 |
CMC | CF取反 |
STD | DF<-1 |
CLD | DF<-0 |
STI | IF<-1,开中断 |
CLI | IF<-0,关中断 |
外部同步指令:
- 处理器暂停指令HLT:使处理器处于暂停,只有复位信号(RESET)、外部中断请求(NMI、INTR)可使其退出;常用于等待中断或多处理机的同步操作
- 处理机等待指令WAIT:处理检测\(\overline{\text{TEST}}\) 引脚信号。\(\overline{\text{TEST}}\) 为高电平时处理器空转;\(\overline{\text{TEST}}\) 为低电平时,处理器退出中转,执行后续操作
- 空操作NOP:占一个指令周期,用于调整延时