'Intel 8086指令系统可分为6类:数据传送类指令,算术运算类指令,位操作类指令,控制转移类指令,串操作类指令,处理机控制类指令。
2.1 数据传送类指令
2.1.1 通用数据传送指令
(1)传送指令MOV
MOV DEST, SRC //DEST ← SRC
MOV指令把1字节或字的操作数从源地址SRC传送至目的地址DEST。源操作数可以是立即数、寄存器或主存单元,目的操作数可以是寄存器或主存单元,但不能是立即数。立即数传送例子:
MOV AL, 4 //AL ← 4,字节传送
MOV CX, 0FFH //CX ← 00FFH,字传送
MOV SI, 200H //SI ← 0200H,字传送
MOV BYTE PTR[SI], 0AH //DS:[SI] ← 0AH,BYTE PTR说明是字节操作
MOV WORD PTR[SI + 2], 0BH //DS:[SI + 2] ← 0BH,WORD PTR说明是字操作
在包括传送指令的绝大多数双操作数指令中(除非特别说明),目的操作数与源操作数必须类型一致,或者同为字,或者同为字节,否则为非法指令。例如:
MOV AL, 050AH //非法指令:050AH为字,而AL为字节
8086不允许立即数传送至段寄存器,例如:
MOV DS, 100H //非法指令:不允许立即数至段寄存器的传送
寄存器传送例子:
MOV AX, BX
MOV AH, AL
MOV DS, AX
MOV [BX], AL
存储器传送例子:
MOV AL, [BX]
MOV DX, [BP]
MOV ES, [SI]
8086指令系统除串操作类指令外,不允许两个操作数都是存储单元,所以没有主存至主存的数据传送,要实现这种传送可通过寄存器间接实现。
例如将buffer1单元的数据传送至buffer2单元,buffer1和buffer2是两个字变量:
MOV AX, buffer1
MOV buffer2, AX
不能向CS段传送数据,因为改变CS值将导致程序执行混乱
MOV CS, [SI] //非法指令
段寄存器传送例子:
MOV [SI], DS
MOV AX, ES
MOV DS, AX
不允许段寄存器之间的直接数据传送,例如:
MOV DS, ES //非法指令:不允许seg ← seg传送
(2)交换指令XCHG
交换指令用来将源操作数和目的操作数内容交换,例如:
MOV AX, 1234H //AX = 1234H
MOV BX, 5678H //BX = 5678H
XCHG AX, BX //AX = 5678H, BX = 1234H
XCHG AH, AL //AX = 7856H
XCHG AX, [2000H] //等同于XCHG [2000H], AX
XCHG AL, [2000H] //等同于XCHG [2000H], AL
(3)换码指令XLAT
换码指令用于将BX指定的缓冲区中,AL指定的位移处的数据取出赋值给AL,即AL ← DS:[BX + AL]
。
例如将首地址为100H的表格缓冲区的3号数据取出:
MOV BX, 100H
MOV AL, 03H
XLAT
2.1.2 堆栈操作指令
(1)进栈指令PUSH
进栈指令先使堆栈指针SP减2,然后把一个字操作数存入堆栈顶部。堆栈操作的对象只能是字操作数,进栈时,低字节存放于低地址,高字节存放在高地址,SP相应向低地址移动2字节单元。
例如将7812H压入堆栈:
MOV AX, 7812H
PUSH AX
将主存单元DS:[2000H]的一个字压入堆栈:
PUSH [2000H]
(2)出栈指令POP
出栈指令把栈顶的一个字传送至指定的目的操作数,然后堆栈指针SP加2。目的操作数应为字操作数,字从栈顶弹出时,低地址字节送低字节,高地址字节送高字节。
例如将栈顶的一个字的内容弹出送入AX寄存器:
POP AX
将栈顶一个字送入主存DS:[2000H]
POP [2000H]
2.1.3 标志传送指令
(1)标志寄存器传送
标志寄存器传送指令用来传送标志寄存器的内容,包括LAHF/SAHF、PUSHF/POPF指令。
①标志送AH指令LAHF
LAHF指令将标志寄存器FLAGS的低字节送寄存器AH,即状态标志位SF/ZF/AF/PF/CF分别送入AH的第7/6/4/2/0位,而AH的第5/3/1位任意。
LAHF //AH ← FLAGS的低字节
②AH送标志指令SAHF
SAHF将AH寄存器内容送FLAGS的低字节,即SAHF和LAHF是一对功能相反的指令,它们只影响标志寄存器的低8位,而对高8位无影响。
SAHF //FLAGS的低字节 ← AH
③标志进栈指令PUSHF
PUSHF指令将标志寄存器的内容压入堆栈,同时栈顶指针SP减2。这条指令可用来保存全部标志位。
PUSHF
④标志出栈指令POPF
POPF指令将栈顶字单元内容送标志寄存器,同时栈顶指针SP加2.
POPF
(2)标志位操作
标志位操作指令可用来对CF、DF和IF三个标志位进行设置,除影响其所设置的标志外,均不影响其他标志。
CLC //复位进位标志:CF ← 0
STC //置位进位标志:CF ← 1
CMC //求反进位标志:CF ← ~CF
CLD //复位方向标志:DF ← 0
STD //置位方向标志:DF ← 1
CLI //复位中断标志:IF ← 0
STI //置位中断标志:IF ← 1
2.1.4 地址传送指令
(1)有效地址传送指令LEA
LEA指令将存储器操作数的有效地址传送至指定寄存器。
MOV BX, 0400H
MOV SI, 3CH
LEA BX, [BX + SI + 0F62H] //BX ← BX + SI + 0F62H = 0040H + 3CH + 0F62H = 139EH
这里,BX得到的是主存单元的有效地址,不是物理地址,也不是该单元的内容。
(2)指针传送指令
LDS和LES指令将主存中MEM指定的字送至R16,并将MEM的下一字送DS或ES寄存器。实际上,MEM指定了主存的连续4字节作为逻辑地址,即32位的地址指针。
MOV WORD PTR [3060H], 0100H
MOV WORD PTR [3062H], 1450H
LDS SI, [3060H] //DS = 1450H, SI = 0100H
LES DI, [3060H] //ES = 1450H, DI = 0100H
2.2 算术运算类指令
2.2.1 状态标志
CF(进位标志位):当执行一个加法(减法)运算时,最高位产生进位(或借位)时,CF为1,否则为0。
ZF(零标志位):若当前的运算结果为零,则ZF为1,否则为0。
SF(符号标志位):该标志位与运算结果的最高位相同。即运算结果为负,则SF为1,否则为0。
OF(溢出标志位):若运算结果超出机器能够表示的范围称为溢出,此时OF为1,否则为0。判断是否溢出的方法是:进行二进制运算时,最高位的进位值与次高位的进位值进行异或运算,若运算结果为1则表示溢出OF=1,否则OF=0
PF(奇偶标志位):当运算结果的最低16位中含1的个数为偶数则PF=1否则PF=0
AF(辅助进位标志):一个加法(减法)运算结果的低4位向高4位有进位(或借位)时则AF=1否则AF=0
另外还有三个控制标志位用来控制CPU的操作,可以由程序进行置位和复位。
TF(跟踪标志位):该标志位为方面程序调试而设置。若TF=1,8086/8088CPU处于单步工作方式,即在每条指令执行结束后,产生中断。
IF(中断标志位):该标志位用来控制CPU是否响应可屏蔽中断。若IF=1则允许中断,否则禁止中断。
DF(方向标志位):该标志位用来控制串处理指令的处理方向。若DF=1则串处理过程中地址自动递减,否则自动递增。
2.2.2 加法指令
加法指令包括ADD、ADC和INC指令,执行字或字节的加法运算。
(1)加法指令ADD
加法指令ADD将源操作数与目的操作数相加,结果送到目的操作数,支持寄存器与立即数、寄存器、存储单元,以及存储单元与立即数、寄存器间的加法运算。
MOV AL, 0FBH //AL = 0FBH
ADD AL, 07H //AL = 02H
MOV WORD PTR [200H], 4652H //[200H] = 4652H
MOV BX, 1FEH //BX = 1FEH
ADD AL, BL //AL = 00H
ADD WORD PTR [BX + 2], 0F0F0H //[200H] = 3742H
(2)带进位加法指令ADC
ADC指令除完成ADD加法运算外,还要加进位CF,其用法及对状态标志的影响也与ADD指令一样。ADC指令主要用于与ADD指令相结合实现多精度数相加。
MOV AX, 4652H //AX = 4652H
ADD AX, 0F0F0H //AX = 3742H, CF = 1
MOV DX, 0234H //DX = 0234H
ADC DX, 0F0F0H //DX = 0F352H, CF = 0
(3)增量指令INC
INC指令对操作数加1(增量),是一个单操作数指令,操作数可以是寄存器或存储器。
INC BX
INC BYTE PTR [BX]
2.2.3 减法指令
减法指令包括SUB、SBB、DEC、NEG和CMP,执行字或字节的减法运算,除DEC不影响CF标志外,其他减法指令按定义影响全部状态标志位。
(1)减法指令SUB
减法指令SUB使目的操作数减去源操作数,结果送目的操作数,支持的操作数类型同加法指令。
MOV AL, 0FBH //AL = 0FBH
SUB AL, 07H //AL = 0F4H, CF = 0
MOV WORD PTR[200H], 4652H //[200H] = 4652H
MOV BX, 1FEH //BX = 1FEH
SUB AL, BL //AL = 0F6H, CF = 1
SUB WORD PTR[BX + 2], 0F0F0H //[200H] = 5562H, CF = 1
(2)带借位减法指令SBB
带借位减法指令SBB使目的操作数减去源操作数,还要减去借(进)位CF,结果送到目的操作数。SBB指令主要用于与SUB指令相结合,实现多精度数相减。
MOV AX, 4652H //AX = 4652H
SUB AX, 0F0F0H //AX = 5562H, CF = 1
MOV DX = 0234H //DX = 0234H
SBB DX, 0F0F0H //DX = 1143H, CF = 1
(3)减量指令DEC
DEC指令对操作数减1(减量),是一个单操作数指令,操作数可以是寄存器或存储器。
DEC CX
DEC WORD PTR [SI]
INC与DEC指令不影响进位CF标志,但影响其他状态标志。
(4)求补指令NEG
NEG指令也是一个单操作数指令,对操作数执行求补运算,即用零减去操作数(将操作数按位取反后加一),然后将结果返回操作数。
MOV AX, 0FF64H //AX = 0FF64H
NEG AL //AX = 0FF9CH, SF = 1, PF = 1, CF = 1
SUB AL, 9DH //AX = 0FFFFH, SF = 1, PF = 1, CF = 1
NEG AX //AX = 0001H, SF = 0, PF = 0, CF = 1
DEC AL //AX = 0000H, SF = 0, ZF = 1, PF = 1, CF = 1
NEG AX //AX = 0000H, SF = 0, ZF = 1, PF = 1, CF = 0
(5)比较指令CMP
比较指令将目的操作数减去源操作数,但结果不回送目的操作数。也就是说,CMP指令与减法指令SUB执行同样的操作,同样影响标志,只是不改变目的操作数。
例如比较AL是否大于100:
CMP AL, 100 //AL - 100
JB below //若AL < 100,则跳转到below执行
SUB AL, 100 //若AL >= 100,则AL ← AL - 100
INC AH //AH ← AH + 1
below: ...
2.2.4 乘法指令
乘法指令用来实现两个二进制操作数的相乘运算,包括两条指令:无符号数乘法指令MUL和有符号数乘法指令IMUL。乘法指令隐含使用一个操作数AX和DX,源操作数则显式给出。如果是字节量相乘,则AL与R8/M8相乘得到16位的字,存入AX中;如果是字相乘,则AX与R16/M16相乘得到32位的结果,其高字存入DX,低字存入AX。
无符号数0B4H与11H相乘:
MOV AL, 0B4H //AL = 0B4H = 180D
MOV BL, 11H //BL = 11H = 17D
MUL BL //AX = 0BF4H = 3060D, OF = CF = 1(AX高8位不为0)
有符号数0B4H与11H相乘:
MOV AL, 0B4H //AL = 0B4H = -76D
MOV BL, 11H //BL = 11H = 17D
IMUL BL //AX = 0FAF4H = -1292D, OF = CF = 1
2.2.5 除法指令
除法指令执行两个二进制数的除法运算,包括无符号二进制数除法指令DIV和有符号二进制数除法指令IDIV两条指令。除法指令隐含使用DX和AX作为一个操作数,指令中给出的源操作数是除数。如果是字节除法,AX除以R8/M8,8位商存入AL,8位余数存入AH;如果是字除法,DX.AX除以R16/M16,16位商存入AX,16位余数存入DX。
无符号数0400H除以B4H:
MOV AX, 0400H //AX = 400H = 1024D
MOV BL, 0B4H //BL = 0B4H = 180D
DIV BL //商AL = 05H,余数AH = 7CH = 124D
有符号数0400H除以B4H:
MOV AX, 0400H //AX = 400H = 1024D
MOV BL, 0B4H //BL = 0B4H = -76D
IDIV BL //商AL = 0F3H = -13D,余数AH = 24H = 36D
2.2.6 符号扩展指令
8086设计有两条符号扩展指令CBW和CWD。CBW指令将AL的最高有效位
D
7
D_7
D7扩展至AH,即:如果AL的最高有效位是0
,则AH = 00
;AL的最高有效位为1
,则AH = FFH
,AL不变。CWD将AX的内容符号扩展形成DX,即:如果AX的最高有效位
D
15
D_{15}
D15为0
,则DX = 0000H
;如果AX的最高有效位
D
15
D_{15}
D15为1
,则DX = FFFFH
。
MOV AL, 80H //AL = 80H
CBW //AX = 0FF80H
ADD AL, 255 //AL = 7FH
CBW //AX = 007FH
2.3 位操作类指令
2.3.1 逻辑运算指令
逻辑运算指令用来对字或字节按位进行逻辑运算,包括5条指令:逻辑与AND、逻辑或OR、逻辑非NOT、逻辑异或XOR和测试TEST。
(1)逻辑与指令AND
AND指令对两个操作数执行按位的逻辑与运算,结果送至目的操作数。在这两个操作数中,源操作数可以是任意寻址方式,目的操作数只能是立即数之外的其他寻址方式,且两个操作数不能同时为存储器寻址方式。
MOV AL, 45H
AND AL, 31H //AL = 01H, CF = OF = 0, SF = 0, ZF = 0, PF = 0
AND指令可用于复位某些位,不影响其他位,例如将BL中 D 0 D_0 D0和 D 3 D_3 D3清零,其余位不变,则:
AND BL, 11110110B
(2)逻辑或指令OR
OR指令对两个操作数执行按位的逻辑或运算,结果送至目的操作数,所支持的操作数同AND指令。
MOV AL, 45H
OR AL, 31H //AL = 75H, CF = OF = 0, SF = 0, ZF = 0, PF = 0
OR指令可置位某些位,不影响其他位,例如将BL中 D 0 D_0 D0和 D 3 D_3 D3置1,其余位不变,则:
OR BL, 00001001B
(3)逻辑异或指令XOR
XOR指令对两个操作数执行按位的逻辑异或运算,结果送至目的操作数,所支持的操作数同AND指令。
MOV AL, 45H
XOR AL, 31H //AL = 74H, CF = OF = 0, SF = 0, ZF = 0, PF = 1
XOR可用于求反某些位或将寄存器清零:
XOR BL, 00001001B //将BL的第0和3位求反
XOR AX, AX //AX = 0, CF = OF = 0, SF = 0, ZF = 1, PF = 1
(4)逻辑非指令NOT
NOT指令对操作数按位取反,NOT指令是一个单操作数指令,该操作数可以是立即数之外的任何寻址方式,NOT指令不影响标志位。
MOV AL, 45H
NOT AL //AL = 0BAH
(5)测试指令TEST
TEST指令对两个操作数执行按位的逻辑与运算,但结果不返回至目的操作数,只根据结果来设置状态标志。TEST指令通常用于检测一些条件是否满足但又不希望改变原操作数的情况,这条指令之后一般是条件转移指令,目的是利用测试条件转向不同的程序段。
TEST AL, 01H //测试AL的最低位是否为1
JNZ there //如果最低位为1,则ZF = 0,程序转移到there
... //否则最低位为0,则ZF = 1,顺序执行
there: ...
2.3.2 移位指令
移位指令分成逻辑移位指令和算术移位指令,分别具有左移或右移操作。
SHL reg/mem, 1/CL //逻辑左移:reg/mem左移1/CL位,最低位补0,最高位进入CF
SHR reg/mem, 1/CL //逻辑右移:reg/mem右移1/CL位,最高位补0,最低位进入CF
SAL reg/mem, 1/CL //算术左移,功能与SHL相同
SAR reg/mem, 1/CL //算术右移:reg/mem右移1/CL位,最高位不变,最低位进入CF
移位指令的目的操作数可以是寄存器或存储单元。源操作数表示移位位数,当移位位数大于1时,需要用CL寄存器的值来表示。移位指令按照移入的位设置进位标志CF,如果进行1位移动,则按照操作数的最高符号位是否改变,相应设置溢出标志OF:如果移位前的操作数最高位与移位后操作数的最高位不同(有变化),则OF = 1
,否则OF = 0
。当移位次数大于1时,OF不确定。
MOV CL, 4
MOV AL, 0F0H //AL = 0F0H
SHL AL, 1 //AL = E0H, CF = 1, SF = 1, ZF = 0, PF = 0, OF = 0
SHR AL, 1 //AL = 70H, CF = 0, SF = 0, ZF = 0, PF = 0, OF = 1
SAR AL, 1 //AL = 38H, CF = 0, SF = 0, ZF = 0, PF = 0, OF = 0
SAR AL, CL //AL = 03H, CF = 1, SF = 0, ZF = 0, PF = 1
2.3.3 循环移位指令
循环移位指令类似移位指令,但要从一端移出的位返回到另一端形成循环,分为不带进位循环移位和带进位循环移位,分别具有左移或右移操作。
ROL reg/mem, 1/CL //不带进位循环左移
ROR reg/mem, 1/CL //不带进位循环右移
RCL reg/mem, 1/CL //带进位循环左移
RCR reg/mem, 1/CL //带进位循环右移
前两条指令不将进位CF纳入循环位中。后两条指令将进位标志CF纳入循环为中,与操作数一起构成的9位或17位二进制数一起移位。
循环移位指令的操作数形式与移位指令相同,如果仅移动1次,可以用1表示;如果移位多次,则需用CL寄存器表示移位次数。循环移位指令按照指令功能设置进位标志CF,不影响SF、ZF、PF、AF标志。对OF标志的影响,循环移位指令与前面介绍的移位指令一样。
例如把AL最低位送BL最低位,但保持AL不变:
ROR BL, 1
ROR AL, 1
RCL BL, 1
ROL AL, 1
2.4 控制转移类指令
控制转移类指令通过修改CS和IP寄存器的值来改变程序的执行顺序,包括5组指令:无条件转移指令、有条件转移指令、循环指令、子程序指令和中断指令。
2.4.1 无条件转移指令
无条件转移就是无任何先决条件就能使程序改变执行顺序。处理器只要执行无条件转移指令JMP,就使程序转到指令的目标地址处,从目标地址处开始执行那里的指令。
JMP指令可以将程序转移到1MB存储空间的任何位置。根据跳转的距离,JMP指令分为段内转移和段间转移。
段内转移是指在当前代码段64KB范围内转移,因此不需要更改CS段地址,只要改变IP偏移地址。如果转移范围可以用一个8位数(-128~+127之间的位移量)表达,则可以形成“短地址”(short jump);如果地址用一个16位数表达,则形成“近转移”(near jump), ± 32 \pm32 ±32KB范围内。
段间转移是指从当前代码段跳转到另一个代码段,此时需要更改CS段地址和IP偏移地址,这种转移也称为“远转移”(far jump)。转移的目标地址必须用一个32位数表达,叫做32位远指针,它就是逻辑地址。
(1)段内转移,相对寻址
JMP label //IP ← IP + 位移量
(2)段内转移,间接寻址
JMP r16/m16 //IP ← r16/m16
这种形式的JMP指令,将一个16位寄存器或主存单元内容送入IP寄存器,作为新的指令指针,但不修改CS寄存器的内容。例如:
JMP AX //IP ← AX
JMP WORD PTR[2000H] //IP ← [2000H]
(3)段间转移,直接寻址
段间直接转移指令是将标号所在段的段地址作为新的CS值,标号在该段内的偏移地址作为新的IP值。这样,程序就能跳转到新的代码段执行。
JMP FAR PTR label //IP ← label的偏移地址,CS ← label的段地址
(4)段间转移,间接寻址
JMP FAR PTR mem //IP ← [mem],CS ← [mem + 2]
段间间接转移指令用一个双字存储单元表示要跳转的目标地址。这个目标地址存放在主存中连续的两个字单元中,其中低位字送IP寄存器,高位字送CS寄存器。例如:
MOV WORD PTR[BX], 0
MOV WORD PTR[BX + 2], 1500H
JMP FAR PTR[BX] //转移到1500H : 0
2.4.2 条件转移指令
条件转移指令中的条件如下表所示:
助记符 | 标志位 | 说明 |
JZ/JE | ZF=1 | 等于零/相等转移 |
JNZ/JNE | ZF=0 | 不等于零/不相等转移 |
JS | SF=1 | 符号为负转移 |
JNS | SF=0 | 符号为正转移 |
JP/JPE | PF=1 | 1的个数为偶转移 |
JNP/JPO | PF=0 | 1的个数为奇转移 |
JO | OF=1 | 溢出转移 |
JNO | OF=0 | 无溢出转移 |
JC/JB/JNAE | CF=1 | 进位/低于/不高于等于转移 |
JNC/JNB/JAE | CF=0 | 无进位/不低于/高于等于转移 |
JBE/JNA | CF=1或ZF=1 | 低于等于/不高于转移 |
JNBE/JA | CF=0且ZF=0 | 不低于等于/高于转移 |
JL/JNGE | SF≠OF | 小于/不大于等于转移 |
JNL/JGE | SF=OF | 不小于/大于等于转移 |
JLE/JNG | SF≠OF或ZF=1 | 小于等于/不大于转移 |
JNLE/JG | SF=OF且ZF=0 | 不小于等于/大于转移 |
(1)判断单个标志位状态
①JZ/JE和JNZ/JNE利用零标志ZF,判断结果是否为零(或相等)。
例:如果AL最高位为0,则设置AH=0;如果AL最高位为1,则设置AH=FFH(也就是用一段程序实现符号扩展指令CBW的功能)。
TEST AL, 80H //测试最高位
JZ next0 //最高位为0(ZF = 1),转移到next0
MOV AH, 0FFH //最高位为1,顺序执行
JMP done //无条件转向done
next0: MOV AH, 0
done: ...
②JS和JNS利用符号标志SF,判断结果是正是负。
例:计算|X-Y|,X和Y为存放于X单元和Y单元的16位操作数,结果存入RESULT中。
MOV AX, X
SUB AX, Y //AX ← X - Y,下面求绝对值
JNS nonneg //为正数,不需处理,直接转向保存结果
NEG AX //为负数,进行求补,得到绝对值
nonneg: MOV RESULT, AX
③JO和JNO利用溢出标志OF,判断结果是否产生溢出。
例:计算X-Y,X和Y分别为存放于X单元和Y单元中的16位有符号操作数。若溢出,则转移到OVERFLOW处理。
MOV AX, X
SUB AX, Y
JO overflow
... //没有溢出,结果正确
overflow: ... //溢出处理
④JP/JPE和JNP/JPO利用奇偶标志PF,判断结果中“1”的个数是偶数还是奇数。
例:设字符的ASCII编码在AL寄存器中,将字符加上奇校验位。
AND AL, 7FH //最高位置0,同时判断1的个数
JNP NEXT //个数已为奇数,则转向next
OR AL, 80H //否则,最高位置1
next: ...
⑤JC/JB/JNAE和JNC/JNB/JAE,利用进位标志CF,判断结果是否进位或借位。
例:记录BX中1的个数。
XOR AL, AL
again:
TEST BX, 0FFFFH //等价于CMP BX, 0
JE next
SHL BX, 1
JNC again
INC AL
JMP again
...
next: ... //AL保存1的个数
(2)用于比较无符号数高低
例:比较无符号数的大小,将较大的存入RESULT主存单元。
CMP AX, BX //比较AX和BX
JNB next //若AX ≥ BX,转移到next
XCHG AX, BX //若AX < BX,交换
next: MOV RESULT, AX
(3)用于比较有符号数大小
例:比较有符号数大小,将较大的存入RESULT主存单元。
CMP AX, BX //比较AX和BX
JNL next //若AX ≥ BX,转移到next
XCHG AX, BX //若AX < BX,交换
next: MOV RESULT, AX
2.4.3 循环指令
JCXZ label //CX = 0,则转移,否则顺序执行
LOOP label //CX ← CX - 1,若CX ≠ 0,循环:IP ← IP + 位移量,否则顺序执行
LOOPZ/LOOPE label //CX ← CX - 1,若CX ≠ 0且ZF = 1,循环:IP ← IP + 位移量,否则顺序执行
LOOPNZ/LOOPNE label //CX ← CX - 1,若CX ≠ 0且ZF = 0,循环:IP ← IP + 位移量,否则顺序执行
JCXZ指令在CX寄存器为0时退出循环;
LOOP指令首先将计数值CX减一,然后判断计数值CX是否为0。CX不为0则继续执行循环体内的指令;CX为0表示循环结束。LOOPZ和LOOPNZ指令中要求同时ZF为1或0才进行循环,用于判断结果是否为0或相等,以便提前结束循环。
例:记录附加段中STRING字符串包含空格字符的个数。假设字符串长度为COUNT字节,结果存入RESULT单元。
MOV CX, COUNT //设置循环次数
MOV SI, OFFSET string
XOR BX, BX //BX清零,用于记录空格数
JCXZ done //如果长度为0则退出
MOV AL, 20H
again:
CMP AL, ES : [SI]
JNZ next //ZF = 0,不是空格,转移
INC BX //ZF = 1,有空格,空格个数加一
next:
INC SI
LOOP again //字符个数减一,不为零则继续循环
done:
MOV RESULT, BX //保存结果
2.4.4 子程序指令
子程序由主程序执行子程序调用指令CALL来调用;而子程序执行完后用子程序返回指令RET,返回主程序继续执行。CALL和RET指令均不影响标志位。
例:利用子程序完成将AL低4位中的1位十六进制数转换成对应的ASCII码。
//主程序
MOV AL, 0FH //提供参数AL
CALL htoasc //调用子程序
...
htoasc: //子程序
AND AL, 0FH //只取AL的低四位
OR AL, 30H //AL高四位变成3
CMP AL, 39H //判断是0~9还是a~f
JBE htoend
ADD AL, 7 //若为a~f,其ASCII还要加上7
htoend: ret //子程序返回