第六章 算术运算 6.1 十进制数加减运算 在计算机中采用BCD码来表示十进制数。BCD码就是使用四位二进制数表示一位十进制数。 第六章 算术运算 6.1 十进制数加减运算 在计算机中采用BCD码来表示十进制数。BCD码就是使用四位二进制数表示一位十进制数。 在8086/8088系统中,将BCD码分为两种格式: 组合型(压缩型、装配型、PACKED) 非组合型(非压缩型、拆散型、UNPACKED) 组合型:一个字节表示两个BCD码,即两位十进制数。 例如:0010 0011 表示十进制数的23
非组合型:一个字节的低四位表示一个BCD码,而高四位对所表示的十进制数没有影响。常为0000B或0011B。 在计算机中直接实现十进制数的运算有两种方法: 1. 数制转换: 先把十进制数转换为二进制数,然后用计算机中的二进制运算指令进行运算。最后将结果由二进制数转换为十进制数。 2. 直接进行十进制数运算:使用计算机中的BCD码指令进行运算。
在计算机内部实现BCD码运算的方法有两种: (2)用二进制数的加、减、乘、除运算指令对BCD码运算,使用BCD码校正指令对结果校正。在8086/8088系统中就是使用这种方法。
一、BCD码校正指令 8086/8088系统共有六条BCD码校正指令。首先介绍加减运算的校正指令。 1、非组合型加法校正指令AAA 在AAA指令执行前,必须是使用ADD或ADC指令完成了加法,且结果是在AL中。AAA指令对AL中内容进行校正。 校正过程为: 当AL中的低4位>9或者AF=1, 则AL<=(AL)+6, AH<=(AH)+1,AL中高4位清0,AF和CF置1。
例如:从键盘输入两个一位数的十进制数,然后相加,结果放在AH和AL中。 MOV AH,1 INT 21H MOV BL,AL ;BL中为输入的一位十进制数的ASCII码,低4位为该数的BCD码 INT 21H ; AL中为输入的另一位十进制数的ASCII码 MOV AH,0 ADD AL,BL AAA
2、组合型加法校正指令DAA DAA指令实现对二位十进制数进行校正。 在执行DAA指令前,必须是用ADD或ADC完成了加法操作,且加的结果放在AL中。 其校正过程为: 若AL中低4位>9或AF=1,则 AL<=(AL)+6, AF<=1 若AL中高4位>9或CF=1,则AL<=(AL)+60H, CF<=1
例:实现两个4位十进制数的加法4678+2556 NUM1 DB 78H,46H NUM2 DB 56H,25H SUM DB ?,? ...... MOV AL,NUM1 ADD AL,NUM2 ;低字节BCD码相加 DAA ;结果低字节校正 MOV SUM,AL MOV AL,NUM1+1 ADC AL,NUM2+1;高字节BCD码相加 DAA ;结果高字节校正 MOV SUM+1,AL
3、非组合型减法校正指令AAS 执行AAS指令前,必须是用SUB或SBB完成了减法操作,且结果放在AL中。 其校正过程为: 若AL中低4位>9或AF=1,则AL<= (AL)-6 , AH<= (AH)-1,同时将AL中高4位清零,CF和AF置1。 4、组合型减法校正指令DAS 执行DAS指令前,必须是用SUB或SBB完成了减法操作,且结果放在AL中。其校正过程为: * 若AL中低4位>9或AF=1,则AL <=(AL)-6,AF置1; * 若AL中高4位>9或CF=1,则AL<=(AL)-60H,CF置1。
二、十进制数运算程序设计举例 例1 试编制一程序,实现非组合型BCD码减法并显示结果。 设数据段有两个4位十进制数(非组合型BCD码)A1和A2。分别放在以DA1和DA2为首址的存储单元中(低字节放低位,高字节放高位)。 结果存放在以DA3为首址的存储单元中。为了显示方便,结果采用低字节放高位,高字节放低位。 为了表示A1和A2的相对大小,若A1≥ A2,则结果前加‘+’号,否则加‘-’号。 结果的显示使用9号DOS功能调用。
源程序的数据段和堆栈安排如下: DATA SEGMENT DA1 DB 1,2,3,4 DA2 DB 0,1,2,3 DA3 DB 5 DUP(0),'$' DATA ENDS STACK1 SEGMENT PARA STACK DW 20H DUP(0) STACK1 ENDS
MOV SI,0 LEA DI, DA3+4 MOV CX,4 MOV AL, DA1[SI] SBB AL, DA2[SI] AAS MOV AL, ‘+’ CLC LAHF JNC NEXT AND AL, 0FH OR AL, 30H MOV AL, ‘-’ MOV [DI], AL SAHF INC SI DEC DI MOV [DI], AL MOV DX, OFFSET DA3 MOV AH, 09H INT 21H LOOP LOP
CODE SEGMENT ASSUME CS:CODE,DS:DATA,SS:STACK1 START: MOV AX,DATA MOV DS,AX MOV SI,0 LEA DI,DA3+4 ;存结果单元末址送DI MOV CX,4 ;十进制位数送CX CLC LOP: MOV AL,DA1[SI] SBB AL,DA2[SI] ;两数相减 AAS ;校正 LAHF ;暂存向高位的借位 AND AL,0FH ;转换成ASCII码 OR AL,30H MOV [DI],AL ;存结果 SAHF ;恢复向高位的借位 INC SI
DEC DI LOOP LOP MOV AL,‘+ ’ JNC NEXT MOV AL,‘-’ ;有向更高位的借位, 存‘-’号 NEXT: MOV [DI],AL MOV DX,OFFSET DA3 MOV AH,9 ;9号功能调用显示结果 INT 21H MOV AH,4CH CODE ENDS END START
6. 2 乘除法运算 一. 乘除法指令 1、无符号数乘法指令MUL 指令格式:MUL OPRD 6. 2 乘除法运算 一. 乘除法指令 1、无符号数乘法指令MUL 指令格式:MUL OPRD 其中:OPRD提供乘法运算的一个操作数,它只能是寄存器或存储器操作数。另一操作数隐含使用AL或AX寄存器。 运算结果存放在AX(字节运算)或DX:AX(字乘法)中。 字节运算:AX<=(AL) ×(OPRD) 字运算 :DX:AX<= (AX)×(OPRD)
MUL只影响CF和OF标志。若结果的AH(字节运算)或DX(字运算)为全0,CF=0、OF=0,否则 CF=1、OF=1。 2、带符号数乘法指令IMUL 指令格式:IMUL OPRD 该指令的功能除了操作数是带符号外,其余与MUL指令相同。 对标志位的影响:若乘积的高半部AH(字节乘法)或DX(字乘法)是低半部的符号扩展(不是有效数值),则CF=0、OF=0 。否则CF=1、OF=1 。
例如,对字节乘法有: 若乘积的(AH)=11111111,且AL最高为1,则表示符号扩展, 则CF=0、OF=0; 若乘积的(AH)=00000000,且AL最高为0,则表示符号扩展, 则CF=0、OF=0; 若乘积的(AH)=11111110,不是符号扩展, 则CF=1、OF=1; 若乘积的(AH)=00000010,不是符号扩展, 则CF=1、OF=1。
3、无符号数除法指令DIV 指令格式:DIV OPRD 其中OPRD是除法运算中的除数,它可以是字节(字节除法)或字(字除法)操作数,只能是寄存器或存储器操作数。被除数和结果隐含使用以下的寄存器: 字节除法:AL<=(AX)/(OPRD) ,AH<= 余数 字除法:AX<=(DX:AX)/(OPRD), DX<=余数 对标志影响:该指令对标志各位无有效标志影响。 下面两种情况将产生0型中断,转入除法出错处理: (OPRD)=0 商>0FFH(字节运算)或商>0FFFFH(字运算)
4、带符号数除法指令IDIV 除操作数是带符号数外,该指令的功能与DIV指令相同。 当结果商的值超过-127~+127(字节运算)或 -32767~+32767(字运算)范围时,将产生0型中断。 5、字节/字扩展指令CBW/CWD 这两条指令主要用于除法指令前,形成双倍长度的被除数。它们都是无操作数指令,隐含使用AX或DX。 指令功能: CBW:扩展AL中符号位到AH中 CWD:扩展AX中符号位到DX中 两条指令对标志都无影响。
二、乘除运算程序设计举例 例1 设数据段有X、Y两变量(无符号数),且XY不超过一个字的表示范围(65535),试编制一计算XY的程序。 由于没有乘方指令,因此需要将乘方转换为乘法运算。即: XY=X×X………. × X Y
源程序的数据段和堆栈安排如下: DATA SEGMENT VARX DW 5 VARY DW 6 POWER DW ? DATA ENDS STACK1 SEGMENT PARA STACK DW 20H DUP(0) STACK1 ENDS
MOV AX,VARX MOV CX,VARY JCXZ EXIT2 DEC CX JE EXIT1 MOV DX,0 LOP 开始 MOV AX,VARX MOV CX,VARY 初始化:AX<=VARX CX<=VARY JCXZ EXIT2 N DEC CX (CX)=0? Y CX<=(CX) - 1 Y JE EXIT1 (CX)=0? N MOV DX,0 LOP DX<=0 MUL VARX AX<=(AX)*X CX<=(CX) - 1 MOV POWER,1 LOOP LOP N (CX)=0? EXIT2 EXIT1 Y 存结果:POWER<=1 存结果:POWER<=(AX) MOV POWER,AX 结 束
CODE SEGMENT ASSUME CS:CODE,DS:DATA,SS:STACK1 MATH: MOV AX,DATA MOV DS,AX MOV AX,VARX MOV CX,VARY JCXZ EXIT2 DEC CX JE EXIT1 MOV DX,0 ;乘积的高位清0 LOP: MUL VARX LOOP LOP EXIT1: MOV POWER, AX;存结果 JMP RESULT EXIT2: MOV POWER, 1 ;X0=1 RESULT:MOV AH,4CH INT 21H CODE ENDS END MATH
6.4 多精度数运算 8086/8088微处理器的每条指令只能处理8位或16位二进制数,其表示的数值范围有限,有时不能满足需要。为了提高运算精度,常用多字节或多字来表示一个完整的数据。 例1 试编写对一个多精度数求补的子程序。 设多精度数的首址放在SI中(低字节放低地址,高字节放高地址),多精度数的字节数在CL中。程序中,求补采用的是“变反加1”的方法。
COMP PROC MOV CS:RESV,SI ;暂存多精度数首址 XOR CH,CH MOV AL,CL ;暂存多精度字节数 LOP1: NOT BYTE PTR [SI] ;变反 INC SI LOOP LOP1 MOV SI,CS:RESV ;恢复多精度数首址 MOV CL,AL ;恢复字节数 STC ;置CF为1 LOP2: ADC BYTE PTR [SI],0 ;完成最低位加1 LOOP LOP2 RET RESV DW 0 COMP ENDP
例2 编制一个多精度数的循环左移子程序。 1单元 2单元 N单元 为了将最后一个单元(N单元)的最高位移入第一个单元(1单元)的最低位,先测试N单元的最高位,将测试的结果记录在CF中,在1单元做循环左移时,将其移入最低位。 设多精度数的首址在SI中,字节数在CL中。
ROTATE PROC XOR CH,CH MOV BX,CX TEST BYTE PTR [BX-1][SI],80H ;测试最后单元的最高位,并清CF JNS LOP ;最高位=0,保持CF=0 STC ;最高位=1,CF<=1 LOP: RCL BYTE PTR [SI],1 ;循环左移一位 INC SI LOOP LOP RET ROTATE ENDP
例3 用乘法指令实现32位(双字长)二进制数的乘法 设被乘数两个字用a、b表示,乘数两个字用c、d表示。乘积则为64位(4个字长)。由于乘法指令只能完成单字乘法,对于双字乘法的处理过程如下图所示。 a b × c d b*d a*d b*c a*c R0 R1 R2 R3 乘积高字 乘积低字 设乘数和乘积存放为:低字存于高地址单元,高字存于低地址单元。
TITLE 32-BIT MULTIPLICATION DATA SEGMENT NUM1 DW 1220H,48A2H NUM2 DW 2398H,0AE41H PRODU DW 4 DUP(0) DATA ENDS STACK1 SEGMENT PARA STACK DW 20H DUP(0) STACK1 ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA,SS:STACK1 START: MOV AX,DATA MOV DS,AX XOR DX,DX MOV AX,NUM2+2 MUL NUM1+2 ;完成b*d MOV PRODU+6,AX ;存R0 MOV PRODU+4,DX ;暂存R1的部分 a b × c d b*d a*d b*c a*c R3 R2 R1 R0 PRODU PRODU+4 PRODU+2 PRODU+6 乘积高字 乘积低字
INT 21H CODE ENDS END START MOV AX,NUM2+2 MUL NUM1 ;完成a*d ADD PRODU+4,AX; ADC PRODU+2,DX ADC PRODU,0 ;将进位送PRODU中 MOV AX,NUM2 MUL NUM1+2 ;完成b*c ADD PRODU+4,AX;完成R1存放 ADC PRODU,0 MUL NUM1 ;完成a*c ADD PRODU+2,AX ;完成R2存放 ADC PRODU,DX ;完成R3存放 MOV AH,4CH INT 21H CODE ENDS END START a b × c d b*d a*d b*c a*c R3 R2 R1 R0 PRODU PRODU+4 PRODU+2 PRODU+6 乘积高字 乘积低字