一、8086的寻址方式(以MOV指令为例)
①计算机指令,通常包含操作码(Opcode)和操作数(Operand)两部分,操作码指出操作的性质,操作数给出操作的对象。
②寻址方式就是指令中说明操作数所在地址的方法。 ③8086寻址空间为1MB。
④指令有单操作数、双操作数和无操作数之分。如果是双操作数,要用逗号分开,左边的为目的操作数,右边的为源操作数。
⑤立即数寻址可直接从指令队列中取数,指令执行速度较快; 寄存器寻址操作数在寄存器中,执行速度最快;
存储器寻址操作数在存储器中,又分几种形式.执行速度较慢;
1-1 立即数操作数→立即寻址
- 立即数只能作源操作数,不能作目的操作数。
- 操作数直接包含在指令中,它是一个8位或16位的常数,也叫立即数。
- 立即数可以送到寄存器中,还可送到一个存储单元(8位)中或两个连续的存储单元(16位)中去。
- 以A~F开头的16进制数字出现在指令中时,前面一定要加一个数字0。
- 例1:MOV AL,26H ;将8位立即数26H送到AL寄存器中。(用分号加注释)
- 例2:MOV CX,2A50H ;将立即数2A50H送到CX中
- 易错点:在给定代码段中如果题目要求写出程序执行结果,一定看清是AH、AL还是AX,AH、AL只能传送8位数据,AX传送16位数据
1-2 寄存器操作数→寄存器寻址
- 操作数在寄存器中,由指令指定寄存器的名称。
- 16位寄存器可以是;AX、BX、CX、DX、SI、DI、SP、BP。 8位寄存器为:AH、AL、BH、BL、CH、CL、DH、DL
- 例1:MOV DX,AX ;设指令执行前AX=3A68H,DX=18C7H,则指令执行后 DX=3A68H,AX=3A68H(保持不变)
- 例2:MOV CL,AH ;将AH中的8位数据传送到CL寄存器。
- 易错点:源操作数的长度必须与目的操作数一致,否则会出错。
1-3 存储器操作数
1-3-1 直接寻址方式
(1)例1:MOV AX,[2000H] ;
- ①指令中直接给出有效地址EA,这里EA=2000H,必须加[],表示不是立即数。
- ②设DS=3000H,则源操作数的物理地址=16×3000H+2000H=32000H
- ③因目的操作数是16位寄存器AX,所以将存储单元中的一个字送进AX。
- ④若(32000H)=34H,(32001H)=12H,则执行指令后,AX=1234H。
- ⑤高字节存放在高地址单元中,低字节存放在低地址单元中。
(2)如果要对代码段、堆栈段或附加段寄存器所指出的存储区进行直接寻址,应在指令中指定段超越前缀。
- 例如,数据若放在附加段中,则应在有效地址前加说明符“ES:”.计算物理地址时要用ES作基地址,而不是默认值DS.
- 例2:MoV AX,ES:[500H] ;该指令的源操作数的物理地址=16×ES+500H。
(3)允许用符号地址代替数值地址,也就是给存储单元起一个名字。如AREA1,寻址时只要使用其名字,不必记住具体数值。 - 例3:Mov AX,AREA1 ;指令执行后,将从有效地址为AREA1的存储单元中取出一个宁送到AX中去。
(4)程序中事先应用说明语句也叫做伪指令来加以说明。 - 例4:AREA1 DW 0867H
…
MOV AX,AREAL
这里的DW伪指令语句用来定义变量。MOV指令执行后将AREA1单元中内容送到AX,结果AX=0867H.
1-3-2 寄存器间接寻址方式
(1)指令中给出的寄存器中的值不是操作数本身,而是操作数的有效地址EA。
(2)寄存器名称外必须加方括号,可用的寄存器有:BX、BP、SI、DI。
(3)应遵守以下约定:
- 约定1:如果指令中指定的寄存器是BX、SI或DI,则默认操作数存放在数据段中,物理地址=16×DS+BX或=16×DS+SI或=16×DS+DI
- 约定2:如果指令中用BP进行间接寻址,则默认操作数在堆栈段中。
例如:MOV AX,[BP] ;操作数的物理地址=16xSS+BP - 指令中也可以指定段超越前缀。
例如:MOV BX,DS:[BP] ;源操作数物理地址
1-3-3 寄存器相对寻址方式
- 与寄存器间接寻址十分相似。但在有效地址上还要加一个8/16位的位移量。
- 例3.12MOV BX,COUNT[SI]
设DS=3000H,SI=2000H,位移量COUNT=4000H,(36000H)=5678H,则:
物理地址=16×DS+SI+COUNT=30000HI+2000H+4000H=36000H
执行结果BX=5678H
上述指令也可用MOV BX,[COUNT+SI]这种形式来表示。
1-3-4 基址变址寻址方式
- 有效地址是一个基址寄存器(BX或BP)和一个变址寄存器(SI或DD)的内容之和,两个寄存器均由指令指定。
- 若基址寄存器为BX时,段址寄存器用DS,则:物理地址=16*DS+BX+SI或=16×DS+BX+DI
- 若基址寄存器为BP时,段址寄存器应使用ss,则:物理地=16xSS+BP+SI或=16xSS+BP+DI
- 例1: MOV AX,[BX][SI]
设DS=3000H,BX=1200H,S=0500H,(31700H)=ABCDH,则:
物理地址= 16×DS+BX+SI=30000H+1200H+0500H=31700H
执行结果:AX=ABCDH
1-3-5 相对基址变址寻址方式
- 有效地址是基址和变址寄存器的内容,再加上8/16位位移量之和。
- 当基址寄存器为BX时,用DS作段寄存器,则:物理地址=16XDS+BX+SI+8位或16位位移量或=16×DS+BX+DI+8位或16位位移量
- 当基址寄存器为BP时,应使用SS作段寄存器。则:物理地址=16xSS+BP+SI+8位或16位位移量或=16xSS+BP+DI+8位或16位位移量
- 例31 Mov AX,MASK [Bx][s1]
设DS=2000H,BX=1500H,SI=0300H,MASK=0200H,(21A00H)=26BFH,则:物理地址=16×DS+BX+SI+MASK=2000OH+150OH+030OH+0200H=21A0OH
执行结果:AX=26BFH
1-3-6 注意:
(1)涉及操作数的地址时,常使用方括号,带[]的地址必须遵循下列规则:
- 立即数可以出现在方括号内,表示直接地址,例如:[2000H].
- 只有BX、BP、SI、DI可以出现在内,既可单独出现,也可几个寄存器组合(只能相加)。或寄存器与常数相加。但BX和BP不允许出现在同个内.SI和DI也不能同时出现。
- 方括号有相加的含义,故下面几种写法是等价的:6[BX] [SI] l/[BX+6][SI] /[BX+S1+6]
- 若[]内包含BP,则隐含使用SS提供基地址,它们的物理地址=16xSS+EA.
(2)包含BP的操作数有3种形式:①DISP [BP+SI] ;EA=BP+SI+DISP②DISP [BP+DI] ;EA=BP+DI+DISP③ DISP [BP] ;EA=BP+DISP,其中,DISP表示8位或16位位移量,也可以为0。
注:这种情况下。也允许用段超越前缀将SS修改为Cs、DS或ES中的一个,计算物理地址时,应将上式中的Ss改为相应的段寄存器。其余情况均隐含使用DS提供基地址,它们的物理地址=16xDS+EA
1-4 隐含寻址
- 指令中不指明操作数,有隐含规定的寻址方式。
- 例如:DAA ;它对AL中的数据进行十进制调整,结果仍保留在AL中。
1-5 I/O端口寻址
- 直接端口寻址方式:端口地址由指令直接提供,它是一个8位立即数n= 00~ FFH。
- 间接端口寻址方式:被寻址的端口号由寄存器Dx提供,端口号0000~FFFFH。
注:上述寻址方式都针对源操作数。目的操作数也可用除了立即寻址方式之外的所有寻址方式指定,所以一条指令可以有几种寻址方式。
二、8086的指令系统
2-1 数据传送指令
2-1-1 通用数据传送指令
(1)MOV:字节或字的传送
指令格式:MOV 目的,源
指令功能:目的操作数⬅源操作数
- 立即数不能送段寄存器,不能作目的寄存器。
- 源操作数和目的操作数只能有一个需计算两存储单元之间不能传数据。两段寄存器之间不能传送数据。
- IP既不能用作源操作数也不能作目的操作数,CS不能做目的操作数。
- 指令中至少要有一项明确说明传送的是字节还是字。
- 例1:MOV AL,‘B’ ;AL⬅将字符B的ASCII码(42H)
- 例2:MOV AX,DATA
MOV DS,AX
由于DATA表示数据段的段址,是一个16位立即数,不能被直接送进DS,需要先送进另一个数据寄存器(如AX),再传到DS中。 - 在汇编语言程序中,数据通常存放在数据段中。
数据段以段说明符SEGMENT开始,ENDS结束,DATA是数据段的段名。 DB伪操作符用来定义字节变量。
DW定义字变量,低字节在前,高字节在后。DUP复制操作符,前面的“3”说明在存储器中保留3个字节单元,初值均为0。
例如,下面是某个程序的数据段:
DATA SEGMENT ;数据段开始
AREA1 DB 14H,3BH
AREA2 DB 3 DUP(O)
ARRAY DW 3100H,01A6H
STRING DB ‘GOOD’
DATA ENDS ;数据段结束
AREA1的偏移地址为0000H
AREA2的偏移地址为0002H
ARRAY的偏移地址为0005H
字符串‘GOOD’从0009H开始存放
- 例3:MOV DX,OFFSET ARRAY
将ARRAY的偏移地址送到DX,其中,OFFSET为属性操作符,表示应把其后的符号地址的值(而不是内容)作为操作数。 - 例4:MOV AX,TABLE [BP][DI]
;将地址为16×SS+BP+DI+TABLE的字单元中的内容送进AX。
(2)PUSH:入栈指令
指令格式:PUSH 源
指令功能:将源操作数推入堆栈
- 源操作数可以是16位通用寄存器、段寄存器或存储器中的数据字,但不能是立即数。
- 执行PUSH操作后,使SP⬅SP-2,再把源操作数压入SP指示的位置上。
(3)POP:出栈指令
指令格式:POP目的
指令功能:把当前SP所指向的一个字送到目的操作数中
- 目的操作数可以是16位通用寄存器、段寄存器或存储单元,但不能是CS。
- 每执行一次出栈操作,SP⬅SP+2,SP向高地址方向移动,指向新的栈顶。
(4)XCHG:交换字或字节
指令格式:XCHG目的,源
指令功能:源操作数和目的操作数相交换
- 交换可以在寄存器之间、寄存器与存储器之间进行,但段寄存器不能作为操作数,也不能直接交换两个存储单元中的内容。
- 例1: 设AX=2000H,DS=3000H,BX=1800H,(31A00H)=1995H,执行指令
XCHG AX,[BX+200H]
源操作数物理地址=3000×10H+1800H +200H=31A00H
其中数据(31A00H)=1995H
指令执行后,AX=1995H,(31A00H)=2000H
(5)XLAT:表转换
指令格式:XLAT 转换表 ; “转换表”为表格首地址或XLAT ; “转换表”可省略不写;
指令功能:将1个字节从一种代码转换成另一种代码
- 使用XLAT指令前,应建立一个表格,最多256个字节,且置:BX⬅转换表始址,AL⬅表头地址到要找的某项间的位移量;
- 指令执行时,根据位移量从表中查到转换后的代码值,送入AL中。
2-1-2 输入输出指令
(1)IN:输入
指令格式:
①IN AL,端口地址 ; AL一从8位端口读入1字节
或IN AX,端口地址 ; AX一从16位端口读入1个字
②IN AL,DX ﹔端口地址存放在DX中
或IN AX,DX
- 格式①,端口地址(00~FFH)直接包含在IN指令里,共允许寻址256个端口。
- 当端口地址大于FFH时,必须用格式②寻址,即先将端口号送入DX,再执行输入操作,DX允许范围0000~ FFFFH.
- IN指令中也可用符号表示地址。例如,要求从一个模/数(A/D)转换器读入1字节数字量到AL中。
ATOD EQU 54H ;A/D转换器端口地址为54H
IN AL,ATOD ;将54H端口的内容读入AL中
(2)OUT:输出
指令格式:
①OUT端口地址,AL ;8位端口⬅AL内容
或OUT端口地址,AX ;16位端口⬅AX内容 ; ②OUT DX,AL ; DX=端口地址
或OUT DX,AX
2-1-3 地址目标传送指令
这是一类专用于传送地址码的指令,可以用来传送操作数的段地址和偏移地址。
(1)LEA:装入有效地址
指令格式: LEA目的,源
指令功能:取源操作数地址的偏移量,送到目的操作数
- 源操作数必须是存储单元,目的操作数是一个除段寄存器之外的16位寄存器。
- 例3.33设:SI-1000H,DS=5000H,(51000H)=1234H,
指令执行结果如下:
LEABX,[SI] ;[SI的偏移地址为1000H,BX⬅1000H
MoV BX,[SI]
;偏移地址为1000H单元的内容为1234H
;指令执行后,BX⬅1234H
(2)LDS:装入数据段寄存器
指令格式:LDS目的,源
指令功能:从源操作数指定的存储单元中,取出1个4字节地址指针,送进目的寄存器DS和指令中指定的目的寄存器中。
- 源操作数必须是存储单元,目的操作数必须是16位寄存器,常用SI寄存器,但不能用段寄存器。
- 例:设:DS=1200H,(12450H)=F346H,
(12452H)=0A90H。执行指令:
LDS SI,[450H]
结果:存储单元前2字节内容为F346H,SI⬅F346H
后2字节内容为0A90H,DS⬅0A90H
(3)LES:装入附加段寄存器
指令格式:LES目的,源
指令功能:与LDS指令的操作基本相同,但段寄存器为ES,目的操作数常用DI。
- 例:设DS=0100H,BX=0020H,(01020H)=0300H,(01022H)=0500H
LES DI,[BX]
;存储单元前2字节内容为0300H ,SI⬅0300H
;后2字节内容为0500H ,ES⬅0500H
2-1-4 标志传送指令
(1)LAHF:标志寄存器低字节装入AH
指令格式:LAHF
指令功能:把标志寄存器的SF、ZF、AF、PF和CF传送到AH寄存器的相应位。
(2)SAHF:AH内容装入标志寄存器低字节
指令格式:SAHF
指令功能:把AHI内容存入标志寄存器。指令功能与LAHF的操作相反。
(3)PUSHF:标志寄存器入栈指令
指令格式:PUSHF
指令功能:把整个标志寄存器的内容推入堆栈,并使 SP⬅SP-2
(4)POPF:出栈,并送入标志寄存器
指令格式:POPF
指令功能:把SP所指的一个字,传送给标志寄存器 FLAGS,并使SP⬅SP+2。
2-2 算术运算指令
- 可处理4种类型的数:无符号二进制整数、带符号二进制整数、无符号压缩十进制整数、:无符号非压缩十进制整数
- 二进制数可以是8位或16位,如果是带符号数,则用补码表示。
- 压缩十进制数在一个字节中存放两个BCD码十进制数。
- 非压缩十进制数低半字节存放一个十进制数,高半字节为全零。
2-2-1 加法
(1)ADD:加法
指令格式:ADD 目的,源
指令功能:目的⬅源+目的
(2)ADC:带进位的加法
指令格式:ADC 目的,源
指令功能:目的⬅源+目的+CF
- 它们的源操作数可以是寄存器、存储器或立即数。
- 目的操作数只能用寄存器和存储单元。
- 源和目的操作数不能同时为存储器,而且它们的类型必须一致,即都是字节或字。
- 例:ADD AL,18H ; AL⬅AL+18H
ADC BL,CL ;BL⬅BL+CL+CF
ADC AX,DX ; AX⬅AX+DX+CF
ADD AL,COST[BX]
;将AL内容和物理地址=DS: (COST+BX)
;的存储字节相加,结果送到AL中
ADD COST [BX],BL
;将BL与物理地址=DS:(COST+BX)的存储
;字节相加,结果留在该存储单元
它们影响标志位:CF、OF、PF、SF、ZF和AF
(3)INC:增量
指令格式:INC目的
指令功能:目的⬅目的+1
- 目的操作数可以是通用寄存器或内存。指令执行后影响AF、OF、PF、SF和ZF,但进位标志CF不受影响。
- 例:
INC BL ;BL寄存器中内容增1
INC Cx ;CX寄存器中内容增1
指令中只有一个操作数,如果是内存单元,则要
用PTR操作符说明是字还是字节。
(4)AAA:加法的ASCII调整
指令格式:AAA
指令功能:用ADD或ADC指令对两个非压缩BCD数或以ASCII码表示的十进制做加法后,结果在AL中,用此指令将AL中的结果进行调整。 另外,若AF=1,表示有进位,则进到AH中。
(5)DAA:加法的十进制调整
指令格式:DAA
指令功能:对两个压缩BCD数相加后的结果(已在 AL中)进行调整。
注意:要对AL中高半字节和低半字节分别进行调整
2-2-2 减法
(1)SUB:减法
指令格式:sUB目的,源
指令功能:目的⬅目的-源
- 例:SUB AX,BX ; AX⬅AX-BX
SUB DX,1850H ;DX.—DX-1850H
SUB BL,[BX] ;BL中内容减去物理地址=DS: BX处的字节,结果存入BL
(2)SBB:带借位的减法
指令格式:SBB 目的,源
指令功能:目的⬅目的-源-CF
SBB主要用于多字节减法中
(3)DEC:减量
指令格式:DEC 目的
指令功能:目的⬅目的-1
- 例:DEC BX ;BX⬅BX-1
DEC WORD PTR[ BP] ;堆栈段中位于[B],偏置处的字减1
(4)NEG:取负
指令格式:NEG 目的
指令功能:目的⬅0-目的
(5)CMP:比较
指令格式:CMP 目的,源
指令功能:目的-源
注:结果不回送到目的,仅反映在标志位上。
- CMP AL,80H ;AL与80H作比较
CMP BX,DATA1 ;BX与数据段中偏移量为DATA1处的字比较 - 比较指令主要用在希望比较两个数的大小,而又不破坏原操作数的场合。
(6)AAS:减法的ASCII调整
指令格式:AAS
指令功能:在用SUB或SBB指令,对两个非压缩BCD数,或以ASCII码表示的十进制数相减后,对AL中所得结果进行调整,如有借位,则CF置1。
- 例:设AL=BCD 3,CL=BCD 8,求两数之差。很显然,结果为BCD 5,但要向高位借位。
(7)DAS:减法的十进制调整
指令格式:DAS
指令功能:在用SUB或SBB指令,对两个压缩BCD数相减((结果已存在AL中)后,进行调整。同样,它也要对AL中高半字节和低半字节分别进行调整。
- 例:设AL=BCD 56,CL=BCD 98,求两数之差。
2-2-3 乘法
(1)MUL:无符号数乘法
指令格式:MUL源
指令功能:把源操作数和累加器中的数,都当成无符号数,然后将两数相乘。
- 其中有一个操作数一定是累加器。
- 如果源操作数是1个字节,则AX⬅AL*源
- 若源操作数是1个字,则(DX,AX)⬅AX*源
- 源操作数可以是寄存器或存储单元,不能是立即数
- 当源操作数是存储单元时,应在操作数前加BYTE或wORD,说明是字节还是字。
- 例:MUL DL ; AX⬅ALDL
MUL CX ;(DX,AX)⬅AXCX
MUL BYTE [SI] ;AX)⬅AL*(内存中某字节),BYTE说明字节乘法
MUL WORD [BX] ;(DX,AX)⬅AX*(内存中某字),WORD说明字乘法 - MUL指令执行后影响CF和OF标志。
- 如果结果的高半部分不为零,则CF、OF均置1。否则,CF、OF均清0。
- 通过测试这两个标志,可检测并去除结果中的无效前导零。
(2)IMUL:整数乘法
指令格式:IMUL 源
指令功能:把源操作数和累加器中的数,都作为带符号数,进行相乘。
- 存放结果的方式与MUL相同,最后给乘积赋予正确的符号。
- 指令执行后,如果乘积的高半部分不是全0或全1,则置CF=1,OF=1。若结果高半部分为全0或全1,则使CF=0,OF=0。这样来决定是否需要保存积的高半部分。
(3)AAM:乘法的ASCII调整
指令格式:AAM
指令功能:对存于AL的两个非压缩BCD数相乘的积进行调整,结果在AX中,高位放AH,低位在AL。
- 两个ASCII码数相乘前,应先屏蔽掉每个数字的高半字节。
- 调整过程:把AL内容除以10,商放在AH中,余数在AL中。即AH⬅AL/10所得的商,AL⬅AL/10所得的余数。
- 指令执行后,将影响ZF、SF和PF。
- 如果AL和BL中分别存放9和6的ASCII码,则求两数之积时要用以下指令实现:
AND AL,OFH ;屏蔽高半字节
AND BL,OFH
MUL BL ; 相乘
AAM ;调整
如要将结果转换成ASCII码,可再用指令OR AX,3030H来实现,使AX=3534H。 - 8086/8088指令系统中,不允许采用压缩十进制数做乘法,乘法调整指令仅此一条。
2-2-4 除法
(1)DIV:无符号数除法
指令格式:DIV 源
指令功能:对两个无符号二进制数进行除法操作。
- 如果源操作数为字节,被除数必须放在AX中,并且有:
AL⬅AX/源(字节)的商
AH⬅AX/源(字节)的余数 - 要是被除数只有8位,必须把它放在AL中,并将AH清0,然后相除。
- 若源操作数为字,被除数必须放在DX和AX中并且有:
AX⬅(DX,AX)/源(字)的商
DX⬅(DX,AX)/源(字)的余数 - 要是被除数只有16位,除数也是16位,则必须将16位被除数送入AX,再将DX清0,然后相除。
- 与被除数和除数一样,商和余数都是无符号数。
(2)IDIV:整数除法
指令格式:IDIV源
指令功能:功能与DIV相同,但操作数都必须是带符号数,商和余数也都是带符号数,而且规定余数的符号和被除数的符号相同。
- 进行除法操作时,如果商超过了目标寄存器AL或AX所能存放数的范围,计算机会自动产生除法错中断,相当于执行了除数为0的运算,所得的商和余数都不确定。
- 对于带符号数除法指令,字节操作时要求被除数为16位,字操作时要求被除数为32位。
- 如果被除数不满足这个条件,不能简单地将高位置0,而应该先用下面的符号扩展指令(SignExtension)将被除数转换成除法指令所要求的格式,再执行除法指令。
(3)AAD:除法的ASCII调整
指令格式:AAD
指令功能:在做除法前把BCD码转换成二进制数。
- 前面介绍的调整指令,都是在用加法、减法和乘法指令后,紧跟着用一条AAA、AAS或AAM指令,对运算结果进行调整。
- 而除法的ASCI调整指令不同,它是在除法之前进行的。
- 在把AX中的两位非压缩BCD数除以一个非压缩BCD数之前,先用AAD指令,把AX中的被除数调整成二进制数,并存入AL,然后才能用DIV指令进行运算。调整的过程为:
AL⬅AH×10+AL
AH⬅00 - 本指令根据AL寄存器的结果影响SF、ZF和PF。
(4)CBW:把字节转换成字
指令格式: CBW
指令功能:把AL中字节的符号位扩充到AH的所有位, 这时AH被称为是AL的符号扩充。
- 如果AL中的D7=0,就将这个0扩展到AH中去,使AH=00H,即
- 如果AL中的D7=1,就将这个1扩展到AH中去,使AH=FFH,即
- CBW指令执行后,不影响标志位。
(5)CWD:把字转换成汉字
指令格式:CWD
指令功能:把AX中字的符号位扩充到DX寄存器的所有位中去。
若AX中的D15=0,则DX⬅0000H,即
若AX中的D15=1,则DX⬅FFFFH,即
2-3 逻辑运算和移位指令:
2-3-1 逻辑运算
(1)NOT:取反
指令格式:NOT 目的
指令功能:目的十目的取反 目的操作数可以是8位或16位寄存器或存储器,对存储器操作数要说明类型。
- NOT指令只有一个操作数,介绍几种用法。
NOT AX ; AX⬅AX取反
NOT BL ;BL⬅BL取反
NOT BYTE PTR [BX] ;对存储器字节单元内容取反后送回该单元
注:
①以下为双操作数指令。源操作数可以是8或16位立即数、寄存器、存储器,目的操作数只能是寄存器或存储器,两个操作数不能同时为存储器。
②指令执行后,均将CF和OF清0,ZF、SF和PF反映操作结果,AF未定义,源操作数不变。
(2)AND:逻辑乘(与)
指令格式:AND目的,源
指令功能:目的⬅目的 ∧ \wedge ∧源
- 主要用于使操作数的某些位保留(和“1”相与),而使某些位清除(和“0”相与)。
- 例:设AX中是数字5和8的ASCII码,即AX=3538H,将它们转换成BCD码,结果仍放回AX。指令如下:
AND AX,0F0FH ;AX⬅0508H。屏蔽高4位,截得低4位
(3)OR:逻辑加(或)
指令格式:OR目的,源
指令功能:目的⬅目的 ∨ \lor ∨源
- 它主要用于使操作数的某些位保留(和“0”相或),而使某些位置1(和“1”相或)。
- 例:设AX中存有两个BCD数0508H,要将它们分别转换成ASCI码,结果仍在AX中。可用如下指令实现:
OR AX,3030H ;AX⬅3538H
(4)XOR:异或
指令格式:XOR目的,源
指令功能:对两个操作数进行按位逻辑异或运算,结果 送回目的操作数,即目的目的 ∀ \forall ∀源
- 用于使操作数的某些位保留(和“0”相异或),而使某些位取反(和“1"相异或)。
(5)TEST:测试
指令格式:TEST 目的,源
指令功能:目的 ∧ \wedge ∧源,并修改标志位,但不回送结果
- 它常用在要检测某些条件是否满足,但又不希望改变原有操作数的情况下。
2-3-2 算术逻辑移位
可对寄存器或存储器中的字或字节的各位进行算术移位或逻辑移位,移动的次数由指令中的计数值决定
(1)SHL/SAL:逻辑/算术左移
指令格式:SAL 目的,计数值
指令格式:SHL目的,计数值
指令功能:以上两条指令的功能完全相同。
- 均将目的操作数的各位左移,每移一次,最低位LSB补0,最高位MSB进标志位CF。移动一次,相当于将目的操作数乘以2。
- 计数值表示移位次数,可以是1。若大于1,则用CL存放,并要事先将次数存入CL。
- 移位次数最多为31(即00011111B)。
(2)SHR:逻辑右移
指令格式:SHR目的,计数值
指令功能:使目的操作数各位右移,每移一次,最低位进 入CF,最高位补0。
- 右移次数由计数值决定,同SAL/SHL指令一样。
- 若目的操作数为无符号数,每右移一次,使目的操作数除以2.
(3)SAR:算术右移
指令格式:SAR目的,计数值
指令功能:每移位一次,最低位进入CF,但最高位(即符号位)保持不变,而不是补0。相当于对带符号数进行除2操作。
- 例: 用SAR指令计算-128/8=-16的程序段如下:
- MOV AL,10000000B ; AL=-128
MoV CL,03H ;右移次数为3
SAR AL,CL ;算术右移3次后, AL=0F0H=-16
2-3-3 循环移位
算术逻辑移位指令,移出的操作数数位均丢失。循环移位指令则把数位从操作数的一端移到其另一端,从操作数中移走的位不会丢失。
(1)ROL:循环左移
指令格式:ROL 目的,计数值
(2)ROR:循环右移
指令格式:ROR 目的,计数值
(3)RCL:通过进位的循环左移
指令格式:RCL 目的,计数值
(4)RCR:通过进位的循环右移
指令格式:RCR 目的,计数值
- 目的操作数可以是8/16位的寄存器操作数或内存操作数,计数值含义同上,即1或由CL指定。
- ROL和ROR为小循环移位指令,没有把CF包含在循环中;RCL和RCR为大循环指令,把CF作为整个循环的一部分参加循环移位。
- CF的值由最后一次被移出的值决定。
2-4 字符串处理指令
- 字符串是指一系列存放在存储器中的字或字节数据。
- 使用字符串操作指令时,可用指令中的源串和目的串名(即操作数)来表明是字节还是字,也可在指令助记符后加B说明是字节,加w说明是字操作,每种指令就都有3种格式。
字符串指令执行时,必须遵守以下的隐含约定:
①源串位于数据段中,源串字符的始址(或末址)为DS:
SI.
②目的串位于附加段中,目的串字符的始址(或末址)为
ES: DI。
③每执行一次字符串指令,指针SI和DI会自动修改,指
向下一待操作单元。
④ DF标志控制字符串处理的方向:
▶
\blacktriangleright
▶DF=0递增。执行一次字节串操作,SI、DI各+1;字
串操作,SI和DI各+2;
▶
\blacktriangleright
▶DF=1递减。执行一次字节串操作,SI、DI各-1;字串操作,SI和DI各-2。
▶
\blacktriangleright
▶STD指令使DF=1,CLD指令使DF=0。
⑤CX=要处理的字符串长度(字节或字数)。
⑥为加快串运算指令的执行速度,可在基本指令前加重复前缀,使数据串指令重复执行。每重复执行一次,SI和DI都根据方向标志自动修改,Cx的值则自动减1。
⑦能与基本指令配合使用的重复前缀有:
▶
\blacktriangleright
▶REP 无条件重复 (Repeat)
▶
\blacktriangleright
▶REPE/REPZ 相等/结果为零则重复 (Repeat while Equal/Zero)
▶
\blacktriangleright
▶REPNE/REPNZ 不相等/结果非零则重复 (Repeat while Not Equal/Not Zero)
(1)字符串传送:MOVS 目的串,源串
指令格式:MOVS 目的串,源串
指令功能:把源串中的一个字节或字,传送目的串中,且自动修改指针SI和DI。
- 利用MOVS指令,能很方便地将数据从内存的某一地址(源地址)传送到另一个地址(目的地址),还能自动修改源和目的地址。若使用重复前缀,可用一条指令传送一批数据。
(2)字符串比较:CMPS 目的串,源串
指令格式:CMPS 目的串,源串
指令功能:将源串中数据减去目的串数据,但不改变两数据串的原始值,结果反映在标志位上。操作后源串和目的串指针会自动修改。
- 常用此指令来比较两个串是否相同,并由其后的条件转移指令,根据CMPS执行后的标志位值,决定程序的转向。
- CMPS指令前可加重复前缀,下面每两条指令功能相同:
▶ \blacktriangleright ▶REPE CMPS;若CX≠0(未比完)和
▶ \blacktriangleright ▶REPZCMPS ; ZF=1(两串相等),则重复
▶ \blacktriangleright ▶REPNE CMPS;若CX≠0(串没有结束)和串不相等
▶ \blacktriangleright ▶REPNZ CMPS;(ZF-O),则重复比较。
(3)字符串扫描:SCAS 目的串
指令格式:SCAS 目的串
指令功能:从AL(字节操作)或AX(字操作)寄存器的内容,减去ES:DI为指针的目的串元素,结果反映在标志位上,但不改变源操作数。串操作后目的串指针DI会自动修改。
- 利用SCAS指令,可在内存中搜索所需要的数据(关键字)。指令执行前,必须事先将它存在AL(字节)或AX(字)中。
- SCAS指令前也可加重复前缀。
(4)字符串装入:LODS 源串
指令格式:LODS源串
指令功能:把数据段中以SI作为指针的串元素,传 送到AL(字节操作)或AX(字操作)中,同时修改SI。
- 为该指令加重复前缀没有意义。因为每重复传送一次数据,累加器中的内容就被改写,执行重复传送操作后,只能保留最后写入的那个数据。
(5)字符串存储:STOS 目的串
指令格式:STOS目的串
指令功能:将累加器AL或AX中的一个字节或字,传送到以ES:DI为目标指针的目的串中,同时修改DI,以指向串中的下一个单元。
- STOS指令与REP重复前缀连用,即执行指令REP STOS,能方便地用累加器中的一个常数,对一个数据串进行初始化。例如,初始化为全0的串。
- 程序中,正负数的存储均使用STOSB指令,该指令必须以SI为源指针,DI为目的指针。
- 但存储负数时,负数区的目的指针在BX中,因此要用XCHG指令将BX内容送进DI,让DI指向负数区,同时也把DI中的正数区目的指针保护起来。
- 执行STOSB指令后,再用XCHG指令将BX和DI交换回来,以便下次转回GOON标号后,LODS指令仍能正确执行。
2-5 控制转移指令
- 通常,程序中的指令都是顺序地逐条执行的,执行顺序由CS和IP决定,每取出一条指令,指令指针IP自动进行调整,指向下一个存储单元。
- 利用控制转移指令可以改变CS和IP的值,从而改变指令的执行顺序。
2-5-1 无条件转移和过程调用指令
(1)JMP:无条件转移
指令格式:JMP目的
指令功能:无条件地转移到目的地址去执行。
- 这类指令又分成两种类型:
▶ \blacktriangleright ▶段内转移或近(NEAR)转移。转移指令的目的地址和JMP指令在同一代码段中,转移时仅改变IP的内容,段地址CS的值不变。
▶ \blacktriangleright ▶段间转移,又称远(FAR)转移。转移指令的目的地址和JMP指令不在同一段中,转移时,CS和IP的值都要改变,程序要转移到另一个代码段去执行。 - 就转移地址提供的方式而言,又可分为两种方式:
▶ \blacktriangleright ▶直接转移。在指令码中直接给出转移的目的地址,目的操作数用一个标号来表示。它又可分为段内直接转移和段间直接转移。
▶ \blacktriangleright ▶间接转移。目的地址包含在某个16位寄存器或存储单元中,CPU必须根据寄存器或存储器寻址方式,间接地求出转移地址。同样,这种转移类型又可分为段内间接转移和段间间接转移。
- 段内直接转移指令
▶ \blacktriangleright ▶段内相对转移指令,目的操作数均用标号表示。
▶ \blacktriangleright ▶转向的有效地址=IP+8位/16位位移量(DISP)。
▶ \blacktriangleright ▶若转移范围在-128~+127字节内,称为短转移,指令中只需要用8位位移量,在标号前加说明符SHORT。
▶ \blacktriangleright ▶若位移量是16位,称为近转移,目的地址与当前P的距离在-32768~+32767字节之间。可加说明符NEARPTR,也可省略。这类指令用得最多。 - 段内间接转移指令
▶ \blacktriangleright ▶转向的16位地址存放在一个16位寄存器或字存储器单元中。
▶ \blacktriangleright ▶用寄存器间接寻址的段内转移指令,转向的地址存放在寄存器中,执行操作:IP⬅寄存器内容。
▶ \blacktriangleright ▶用存储器间接寻址的段内转移指令,先计算出存储单元的物理地址,再从中取一个字送到IP。即IP+字存储单元内容。 - 段间直接转移指令
指令中用远标号直接给出转向的CS:P,程序从一个代码段转到另一个代码段。 - 段间间接转移指令
操作数为存储器,要转移的目的地址CS:IP存放在存储器中。需加说明符DWORD PTR,表示转向地址需取双字。
(2)CALL:过程调用
- 把某些能完成特定功能又常用的程序段,编写成独立模块,称为过程(Procedure)或子程序(Subroutine)。
- 在主程序中用CALL语句调用这些过程,格式为:CALL过程名
- 过程以PROC开头,ENDP结束。过程中要安排一条返回指令RET,过程执行完后能正确返回主程序。
- 若在过程运行中又调用另一过程,称为过程嵌套。
- 主程序和过程在同一代码段,称为近调用,不在同一段则称为远调用。
- 过程调用的寻址方式与转移指令类似,但没有段内短调用。由于调用结束后需返回原程序继续运行,要执行保护和恢复返址操作,比转移复杂。
- CALL指令分两步执行:
第一步:返址入栈,将CALL下面指令的地址推入堆栈
近调用执行的操作:SP⬅SP-2,IP入栈
远调用执行的操作:SP⬅SP-2,CS入栈 ,SP⬅SP-2,IP入栈
第二步:转到子程序入口执行相应的子程序。入口地址由CALL指令的目的操作数提供,寻址方法与JMP指令类似。- 执行过程中的RET指令时,从栈中弹出返址,使程序返回主程序继续执行。也有两种情况:
▶ \blacktriangleright ▶从近过程返回,从栈中弹出1个字→IP,并且使SP⬅SP+2。
▶ \blacktriangleright ▶从远过程返回,先从栈中弹出1个字→IP,并且使SP⬅SP+2;再弹出1个字→CS,并使SP⬅SP+2。
-
段内直接调用和返回
例:CALL PROG-N ;PROG-N是一个近标号
该指令含3个字节,编码格式为:E8 DISP-L DISP-H 设调用前:CS:IP=2000H:1050H,SS: SP-5000H: 0100H,PROG-N与CALL指令之间的字节距离等于1234H(即DISP=1234H)。 则执行CALL指令的过程: ▶ \blacktriangleright ▶SP⬅SP-2
即新的SP=0100H-2=O0FEH
▶
\blacktriangleright
▶返回地址的IP入栈
由于存放CALL指令的内存首地址为CS:IP=2000:1050H,该指令占3字节,所以返回地址为2000:1053H,即IP=1053H。于是1053H被推入堆栈。
▶
\blacktriangleright
▶根据当前IP值和位移量DISP计算出新的IP值,作为子程序的入口地址,即
IP=IP+DISP=1053H+1234H =2287H
▶
\blacktriangleright
▶程序转到本代码段中偏移地址为2287H处执行
RET指令的寻址方式与CALL一样,在本例中也是段内直接调用。执行过程如下:
▶
\blacktriangleright
▶IP⬅(SP和SP+1)单元内容
即返址IP=1053H从栈中弹出
▶
\blacktriangleright
▶SP⬅SP+2
SP=00FEH+2=0100H,即恢复原SP
结果,返回CALL下面的那条指令,即从2000:
1053处继续执行程序
-
段内直接调用和返回
下面是两条段内间接调用指令的例子,返址在寄
存器或内存中。
▶ \blacktriangleright ▶CALL BX
▶ \blacktriangleright ▶CALL WORD PTR[BX+SI]
它们执行的操作分三步,前两步与直接调用相同,第三步不同,具体为:
①SP⬅SP-2
②IP入栈
③IP⬅EA,计算出目的地址的有效地址EA,送入IP,以此转移。 -
段间直接调用
例:CALL FAR PTR PROG_F ;PROG_F是一个远标号该指令含5个字节,编码格式为:9A DISP-L DISP-H SEG-L SEG-H 设调用前:CS: IP=1000:205AH,SS: SP=2500:0050H,标号PROG-F所在单元的地址指针CS:IP=3000:0500H。 存放CALL指令的内存首址为1000:205AH,由于该指令长度为5个字节,所以返回地址应为1000:205FH。 执行远调用CALL指令的过程具体为: ▶ \blacktriangleright ▶SP⬅SP-2 即SP=0050H-2=004EH
▶
\blacktriangleright
▶CS入栈 即CS=1000H入栈
▶
\blacktriangleright
▶SP⬅SP-2 即SP⬅004CH
▶
\blacktriangleright
▶IP入栈 即IP=205FH入栈
▶
\blacktriangleright
▶转子程序入口 将PROG-F的段地址和偏移地址送CS:IP,即CS⬅3000H,IP⬅0500H
▶
\blacktriangleright
▶执行子程序PROG-F
过程PROG-F中的RET指令的寻址方式也是段间直接调用,返回时执行的操作为:
▶
\blacktriangleright
▶SP⬅SP+2
即SP⬅004C+2=004EH
▶
\blacktriangleright
▶IP⬅栈中内容
IP⬅205FH
▶
\blacktriangleright
▶SP⬅SP+2
SP⬅004EH+2=0050H
▶
\blacktriangleright
▶CS⬅栈中内容
CS⬅1000H
所以程序转返回地址CS:IP=1000:205FH处执行。
- 段间间接调用
操作数必须是存储单元,从该单元开始存放的双字表示过程的入口地址,指令中用DWORD PTR说明是对存储单元进行双字操作。
(3)RET:过程返回
8086还有另一种带参数的返回指令,形式为: RET n
n称为弹出值,它让CPU在弹出返回地址后,再从堆栈中弹出n个字节的数据,也就是让SP再加上n。n可以是0000~FFFFH范围内的任何个偶数。
2-5-2 条件转移
- 将上条指令执行后的状态标志,作为测试条件,来决定是否转移。当条件成立,程序转向指令中给出的目的地址去执行;否则,仍顺序执行。
- 条件转移均为段内短转移,转移指令与目的地址必须在同一代码段中。转移距离范围为-128~+127字节。8位偏移量需用符号扩展法扩展到16位后才能与IP相加。
- 在指令中,目的地址均用标号表示,指令格式:条件操作符 标号
- 条件转移指令共18条,归类成直接标志转移和间接标志转移两大类。
(1)JZ/JE等10条指令:直接标志转移
(2)JA/JNBE等8条指令:间接标志转移
- 不在指令助记符中直接给出标志状态位的测试条件,但仍以某一个或几个标志的状态组合为测试条件,若条件成立则转移,否则顺序往下执行。
- 间接标志转移指令共有8条,每条指令都有两种不同的助记符。
- 在无符号数比较测试指令中,指令助记符中的“A”是英文Above的缩写,表示“高于”之意,“B”是英文Below的缩写,表示“低于”之意。
2-5-3 条件循环控制
是一组增强型条件转移指令,用来控制程序段的重复执行,重复次数由以X中的内容决定,转移目标都是短标号,偏移量都是负值,即只能向前转移。均不影响任何标志。
(1)LOOP:CX ≠ \neq = 0则循环
指令格式:LOOP 短标号
指令功能:重复执行一系列指令。
- 重复次数放在CX中,执行一次指令,CX-1。如减1后CX ≠ \neq = 0,转到指令给定的标号处继续循环;CX=0,结束循环,转去执行LOOP指令后的那条指令。
- 一条LOOP指令相当于执行以下两条指令:
DEC CX
JNZ 标号 - 循环操作也可以只含一条指令,即LOOP指令自身,这样的程序段常用来实现延时。
(2)LOOPE/LOOPZ:CX ≠ \neq = 0和ZF=1则循环
指令格式:LOOPE 标号或LOOPZ 标号
指令功能:LOOPE是相等时循环,LOOPZ是结果为0时循环。
- 它们能完成相同功能,具有不同助记符,用来控制重复执行一组指令。
- 指令执行前,先将重复次数送入CX,每执行一次CX-1,若-1后CX ≠ \neq = 0和ZF=1,则转到指令指定的标号处重复执行;若CX=0或ZF=0,便退出循环,执行本指令后的那条指令。
(3)LOOPNE/LOOPNZ:CX ≠ \neq = 0和ZF=0则循环
指令格式:LOOPNE 标号或LOOPNZ 标号 指令功能:LOOPNE是不相等循环,而LOOPNZ
是结果ZF≠1循环,它们也是一对功能相同但形式不一样的指令。
- 指令执行前,应将重复次数送入CX,每执行一次,CX自动-1,若-1后CX ≠ \neq = 0和ZF=0,则转移到标号所指定的地址重复执行;若CX=0或ZF=1,则退出循环,顺序执行下一条指令。
(4)JCXZ:CX= 0则转移
指令格式:JCXZ 标号
指令功能:若CX寄存器为0,则转移到指令中标号 所指定的地址处,否则将往下顺序执行。
- 它不对CX寄存器进行自动减1操作。
- 这条指令用在循环程序开始处。为了使程序跳过循环,只要事先把CX寄存器清0。
2-5-4 中断
- 计算机在执行正常程序过程中,由于某些事件发生,需要暂时中止当前程序的运行,转到中断服务程序去为临时发生的事件服务。中断服务程序执行完,又返回正常程序继续运行。此过程称为中断。
- 8086的中断有两种: 第一种,外部中断或硬件中断,它们从8086的不可屏蔽,中断引脚NMI或可屏蔽中断引脚IN’TR引入。 第二种,内部中断或软中断,是为解决CPU在运行中发生意的外情况或是为便于对程序调试而设置的。
- 此外,也可在程序中安排一条中断指令INT n,利用它直接产生8086的内部中断。
(1)INT:中断
软件中断指令,n为中断类型号,范围0~255。可安排在程序的任何位置上。
(2)INTO:溢出中断
- 当带符号数进行算术运算后,若OF=1,则可由INTO指令产生类型为4的中断;若OF=0,则INTO指令不产生中断。
- 为此,在带符号数进行加减法运算之后,必须安排一条INTO指令, 一旦溢出就能及时向CPU提出中断请求,CPU可做出相应的处理。
(3)IRET:中断返回
中断返回指令IRET。被安排在中断服务程序的出口处,指令执行后,从堆栈中依次弹出程序断点和FLAGS的内容,使CPU继续执行原来被打断的程序。
2-6 处理器控制指令
(1)标志操作指令
- CLC, CMC利STC
利用CLC指令,使进位标志CF清0,CMC指令使CF取反,STC指令则使CF置1。 - CLD和STD
方向标志DF在执行字符串操作指令时用来决定地址的修改方向,CLD指令使DF清0,而STD指令则使DF置1。 - CLI和STI
中断允许标志F决定CPU能否响应可屏蔽中断请求,指令CLI使F清0,禁止CPU响应这类中断。STI使正F置1,允许CPU响应。
(2)外部同步指令
- ESC换码指令(Escape)
指令格式:ESC 外部操作码,源操作数
指令功能:换码指令实现8086对8087协处理器的控制。 - WAIT等待指令(Wait)
通常跟在ESC指令之后。ESC指令执行后,8086CPU处于等待状态,不断检测TEST引脚,若为高电平,则重复执行WAIT指令,处理器处于等待状态;如变为低电平,便退出等待状态,执行下条指令。 - LOCK封锁总线指令(Lock Bus)
可加在任何指令的前面。凡带有LOCK前缀的指令在执行过程中,将禁止其它处理器使用总线。
(3)停机指令和空操作指令
1)HLT停机指令(Halt)
- 使CPU进入暂停状态,当下列情况之一发生时,则脱离暂停状态:
▶ \blacktriangleright ▶在RESET线上加复位信号;
▶ \blacktriangleright ▶在NMI引脚上出现中断请求信号;
▶ \blacktriangleright ▶在允许中断的情况下,在INTR引脚上出现中断请求信号。
程序中常用HLT指令来等待中断的出现。 - NOP空操作或无操作指令(No Operation)
单字节指令,执行时耗费3个时钟周期的时间,但不完成任何操作。