本节内容:算术运算加法和减法指令。
■加法指令:ADD、ADC、INC指令,示例代码inc.asm、add.asm。
■减法指令:SUB、SBB、DEC指令,示例代码dec.asm、sub.asm。
8086计算机提供了加减乘除四种基本算术运算操作指令。可以用于字节或字的运算。操作数可以是无符号整数也可以是有符号整数。有符号数以补码形式表示。加减指令不分有符号数指令和无符号数指令。乘除指令分有符号数指令和无符号数指令。
此外,我们在第十八章学习十进制算术运算调整指令。
■加减运算指令的说明:
(1)有符号整数和无符号整数处理一视同仁,影响标志位:CF、AF、OF、SF、ZF、PF。
(2)可参与加减运算的操作数只有通用寄存器和存储单元可以存放运算结果,如果参与运算的操作数有2个,只有一个用于存放结果。
(3)操作数如果有两个,它们的类型必须一致,即同时为字节或字。
(4)如图8-5所示,存储器操作数可采用四种存储器操作数寻址方式。[BX+SI+位移量]4种组合,即[BX+SI]、[BX+位移量]、[SI]、[位移量]。
图8-5 参与加减运算的操作数
8.2.1 加法指令
■普通加法指令ADD
指令格式:ADD OPRD1,OPRD2 OPRD1=OPRD1+OPRD2
动手实验25 :普通加法指令ADD
如图8-6所示,在debug调试器内输入:
MOV AX,7896H ;AX=7896H,AH=78H,AL=96H ;mov指令不影响标志位
ADD AL,AH ;AL=0EH,AH=78H,AX=780EH ;CF=1,ZF=0,SF=0,OF=0,AF=0,PF=0
ADD AH,AL ;AH=86H,AL=0EH,AX=860EH ;CF=0,ZF=0,SF=1,OF=1,AF=1,PF=0
ADD AL,0F2H ;AL=00H,AH=86H,AX=8600H ;CF=1,ZF=1,SF=0,OF=0,AF=1,PF=1
ADD AX,1234H ;AX=9834H,AH=98H,AL=34H ;CF=0,ZF=0,SF=1,OF=0,AF=0,PF=0
a命令输入上述指令后,输入t命令单步执行,观察加法运算的结果,并注意观察flag标志寄存器各个状态标志位的变化。
提示
FLAG标志寄存器的状态标志位(CF、AF、PF、ZF、SF、OF)用于记录算术逻辑运算指令的结果。这些状态标志位不仅关系到算术逻辑运算结果的正确性,而且还是我们作为条件判断的依据。我们将在"8.6 节程序控制指令"中详细讲解。
图8-6 ADD加法指令
■带进位加法指令ADC
指令格式:ADC OPRD1,OPRD2 ; OPRD1=OPRD1+OPRD2+CF
例如: mov ax,2 mov ax,1 mov al,98h
mov bx,1 add ax,ax add al,al
sub bx,ax adc ax,3 adc al,3
adc ax,1
动手实验26 :带进位加法指令ADC
如图8-7所示,在debug调试器内输入第一组汇编指令:
图8-7 ADC指令
剩余两组指令留给读者独立完成。
举例
ADC 指令用于多字节运算中。
编程计算:1ef000h+201000h,结果存放在AX高16位和BX低16位。
mov ax,001eh
mov bx,0f000h
add bx,1000h
adc ax,0020h
■加1 指令INC
指令格式:INC OPRD ; OPRD=OPRD+1
该指令主要用于调整地址指针和计数器,INC 指令不影响CF 标志位。
动手实验27 :INC 指令
按照下列要求,编写一个inc.asm源程序,并完成编译链接,生成inc.exe可执行程序,并在debug调试器中单步跟踪执行。
假设有100个16位无符号数存放在1234:5678H开始的内存中,现需要求它们的和。设把32位的和保存在DX(高位)和AX寄存器中。
示例代码3 :
;程序名inc.asm
;演示inc指令的使用方法
;==============================================
assume cs:code
code segment
start:
mov ax,1234h
mov ds,ax ;置数据段寄存器
mov si,5678h ;置指针初值
mov ax,0 ;清32位累加和,ax保存低16位值
xor dx,dx ;dx保存高16位值
mov cx,100 ;置数据个数计数器
next:
add ax,[si] ;求和
adc dx,0 ;加上可能的进位值
inc si ;调整指针
inc si
dec cx ;计数器减1
jnz next ;如果不为0,继续累加下一个数据
;退出程序
mov ax,4c00h
int 21h
code ends
end start
请读者参阅第五章16位汇编学习环境,在Notepade++中抄写此程序。如图8-8所示,编译链接生成可以执行文件inc.exe。然后在调试器中调试,单步跟踪,完成程序的运行。
图8-8 INC指令
动手实验28 :加法运算
按照下列要求,编写一个add.asm源程序,并完成编译链接,生成add.exe可执行程序,并在debug调试器中单步跟踪执行。
假设有100个16位无符号数存放在1234:5678H开始的内存中,现需要求它们的和。设把32位的和保存在result变量中。
示例代码4 :
;程序名:add.asm
;功能:计算1234:5678H开始的内存中100个16位无符号整数的和
;注意有进位值,结果保存到result变量中
;======================================================
assume cs:code,ds:data
data segment
result dd ?
data ends
code segment
start:
mov ax,1234h ;取段值
mov ds,ax
;
mov si,5678h ;取偏移
;dx:ax 清零,保存32位累加和
mov ax,0
xor dx,dx ;CF位清零
;
mov cx,100 ;循环次数
next:
add ax,word ptr [si];取一个字
adc dx,0 ;带进位的加法运算
;inc si
;inc si
add si,2 ;si指向下一个字,等同于上面两条语句
dec cx
jnz next ;跳转到next地址处
;切换数据段
mov bx,data
mov ds,bx
;保存结果
mov word ptr result,ax
mov word ptr result+2,dx
;退出程序
mov ax,4c00h
int 21h
code ends
end start
注意示例代码4中的ADC指令,带进位的加法运算时需要注意提前将CF位清零。"xor dx,dx"语句中的xor指令为常用的清CF位指令,将dx清零的同时,CF清零。除了xor指令之外,请读者思考还有哪些指令可以将CF位清零。
8.2.2 减法指令
■普通减法指令SUB
指令格式:SUB OPRD1,OPRD2 OPRD1=OPRD1-OPRD2
动手实验29 :普通减法指令SUB
如图8-9所示,在debug调试器内输入:
MOV BX,1048H ;BX=1048H,BH=10H,BL=48H ;mov指令不影响标志位
SUB BH,BL ;BH=C8H,BL=48H,BX=C848H ;CF=1,ZF=0,SF=1,OF=0,AF=1,PF=0
SUB BL,BH ;BL=80H,BH=C8H,BX=C880H ;CF=1,ZF=0,SF=1,OF=1,AF=0,PF=0
SUB BL,9 ;BL=77H,BH=C8H,BX=C877H ;CF=0,ZF=0,SF=0,OF=1,AF=1,PF=1
SUB BX,1234H ;BX=B643H,BH=B6H,BL=43H ;CF=0,ZF=0,SF=1,OF=0,AF=0,PF=0
图8-9 SUB指令
■带进位减法指令SBB
指令格式:SBB OPRD1,OPRD2 ; OPRD1=OPRD1-OPRD2-CF
动手实验30 :带进位减法指令SBB
计算003E1000H-00202000H,结果放在AX,BX中:
如图8-10所示,在debug调试器内输入:
mov bx,1000h
mov ax,003eh
sub bx,2000h
sbb ax,0020h
图8-10 SBB指令
■减1 指令DEC
指令格式:DEC OPRD ;OPRD=OPRD-1
该指令主要用于调整地址指针和计数器,DEC 指令不影响CF 标志位。
动手实验31 :DEC 指令
按照下列要求,编写一个dec.asm源程序,并完成编译链接,生成dec.exe可执行程序,并在debug调试器中单步跟踪执行。
假设有两个64位数按高高低低原则存放同一个段的两个缓冲区DATA1和DATA2,现计算DATA1-DATA2。结果存放在RESULT中,可能发生的借位保留在CF中:
示例代码5 :
;程序名dec.asm
;演示dec指令的使用方法
;==============================================
assume cs:code,ds:data
data segment
DATA1 dq 0FEDCBA9876543210h ;16进制数字符A~F开头需添加前缀0
DATA2 dq 123456789ABCDEFh
RESULT dq ?
data ends
code segment
start:
mov ax,data
mov ds,ax
mov cx,4 ;64位分成4个字
sub bx,bx ;清指针,同时清CF
next:
mov ax,word ptr data1[bx] ;取减数
sbb ax,word ptr data2[bx] ;带进位减
mov word ptr RESULT[bx],ax ;保存结果
inc bx ;调整指针,指向下一个字节
inc bx
dec cx ;计数器减1
jnz next ;没完,继续
;退出程序
mov ax,4c00h
int 21h
code ends
end start
请读者参阅第五章 16 位汇编学习环境,在Notepade++中抄写此程序。如图8-11所示,编译链接生成可以执行文件dec.exe。然后在调试器中调试,单步跟踪,完成程序的运行。
图8-11 DEC指令
动手实验32 :长整数减法运算
按照下列要求,编写一个sub.asm源程序,并完成编译链接,生成sub.exe可执行程序,并在debug调试器中单步跟踪执行。
假设有两个64位数按高高低低原则存放同一个段的两个缓冲区DATA1和DATA2,现计算DATA1-DATA2。结果存放在RESULT中,可能发生的借位保留在CF中:
示例代码6 :
;程序名:sub.asm
;功能:两个64位数按照高高低低的原则分别存放在DATA1和DATA2两个缓冲区
;计算DATA1-DATA2
;DATA1 = 2000156781234
;DATA2 = 1000212345678
;=================================
assume cs:code,ds:data
data segment
data1 dw 6AB2H,0B2A2H,01D1H,0
data2 dw 334EH,0E14DH,00E8H,0
result dw ?,?,?,?
data ends
code segment
start:
mov ax,data
mov ds,ax
;
xor si,si ;si清零
mov cx,4 ;循环次数
next:
mov ax,data1[si] ;data1取一个字
sbb ax,data2[si] ;减去data2一个字
mov result[si],ax ;保存结果
inc si ;指向下一个字节
inc si ;指向下一个字节
dec cx ;循环次数减一
jnz next ;ZF为0,跳转到next地址处继续循环
;退出程序
mov ax,4c00h
int 21h
code ends
end start
注意
上述示例代码中,长整数data1和data2给出的是十进制数。在数据段的数据定义中,长整数的存储顺序为低地址存储低位数,高地址存储高位数。
请读者将长整数按照低地址存储高位数,高地址存储低位数的方式重写sub.asm程序,并在debug调试器中验证结果的正确性。
■求补指令NEG
指令格式:NEG OPRD ;OPRD==0-OPRD
该指令用于对操作数取补,就是用零减去操作数OPRD,在把结果送回OPRD。
例:NEG AL NEG [SI]
如在字节操作时对-128取补,或字操作时对-32768取补,则操作数没有变化,但OF被置位。操作数可以是8位或16位寄存器或存储单元。影响CF、ZF、SF、OF、AF、PF,一般总使CF置1,除非操作数为0。
动手实验33 :取补指令NEG
如图8-12所示,在debug调试器内输入如下指令,注意观察状态标志位的变化:
mov ax,0ffffh
neg ax
mov ax,1h
neg ax
mov ax,8000h
neg ax OF=1,CF=1
图8-12 NEG指令
■比较指令CMP
指令格式:CMP OPRD1,OPRD2
该指令完成操作数OPRD1减去操作数OPRD2,运算结果不保存,但影响标志CF、ZF、OF、AF、SF、PF。
主要用于比较两个数的关系,是否相等,根据ZF是否置位,判断是否相等;
如果两个操作数是无符号数,则根据CF判断大小;
如果两个操作数是有符号数,则根据OF位判断大小;
动手实验34 :比较指令CMP
如图8-13所示,在debug调试器内输入如下汇编指令,注意观察状态标志位的变化:
mov ax,8
mov bx,3
cmp ax,bx
执行后,(ax)=8,zf=0,pf=1,sf=0,cf=0,0f=0。
图8-13 CMP指令