编写子程序——解决除法溢出问题
新人自己分析的,希望大神给予意见~!
问题阐述:
前面讲过,div指令可以做除法。当进行8位除法的时候,用al存储结果的商,ah存储结果的余数;进行16位除法的时候,用ax存储结果的商,dx存储结果的余数。可是,现在有一个问题,如果结果大于al或ax所能存储的最大值,该怎么办?
当CPU执行div等除法指令的时候,如果发生这样的情况,将引发CPU的一个内部错误,这个错误被称为:除法溢出。
需要编写一个子程序divdw解决上述问题。
子程序的描述
名称:divdw
功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型。
参数:(ax)=dword型数据的低16位,(dx)=dword型数据的高16位,(cx)=除数
返回:(dx)=结果的高16位, (ax)=结果的低16位,(cx)=余数
应用举例:计算1000000/10(F4240H/0AH)
mov ax, 4240h
mov dx, 000fh
mov cx, 0ah
call divdw
结果:(dx)=0001H,(ax)=86A0H,(cx)=0
分析
根据本书提供的公式,以10进制数来作简要分析:
以42÷2为例,42有两部分,一部分为高位4,另一部分为低位2
其中4=(42/10)取整,2=(42/10)取余
分别用4÷2,2÷2得到2和1,分别保存在存储商的高位的寄存器A和存储商的低位的寄存器B(假设A,B两个寄存器只能存一位数字),结果就由(A)×10+(B) = 2×10+1=21
42这个数是一个好处理情况,因为不涉及到高位除以2后会产生余数。
若是52÷2,由于高位5÷2除不尽,除了商以外还有余数1,若直接和上面一样用2÷2,那么结果就会变成21,和42÷2一样,如果这个回答出现在考卷上,那么一定会被扣分的。说明这个余数是不能被舍弃的,根据公式,可以知道商的低位是由
(余数×10+被除数低位)÷除数得到,这里就是(1×10+2)÷2=6,那最后的商为26,这也很符合我们除法的规则,可以用除式试验看看。
16进制也是如此,如FFFF FFFF这个被除数来说,这个数是有FFFF0000+FFFF得到
即FFFF*10000H(十进制65536)+FFFF得到,相应的商的高位就是(高位FFFF÷n)取整,商的低位就是[(高位FFFF÷n)取余+低位FFFF]÷n得到,那么结果最后需要组合一下即高位×10000H+低位得到也就是公式所说的:
X/n=int(H/n)×65536+[rem(H/n)*65536+L]/n
当然附页5还从数学角度利用了等量代换和不等式的性质证明了高位、低位分别除n,一定不会产生溢出,这个比较简单,也和写这个程序没太大关系就不多说了。
寄存器分析:
可以知道,这个程序是有两个参数的,分别用三个寄存器来传递的,在子程序中,需要用到另一个寄存器来保存中间结果,所以需要在调用后保存该寄存器数据。
汇编代码:
assume cs:codesg
stacksg segment
dw 8 dup(0)
stacksg ends
codesg segment
start:
mov ax, stacksg
mov ss, ax
mov sp, 10h ;栈的初始化
mov ax, 4240h
mov dx, 000fh
mov cx, 0ah ;参数的初始化
call divdw
mov ax, 4c00h
int 21h
divdw:
push bx ;之后需要用到,先保存
push ax ;首先计算高位,低位入栈
mov ax, dx
mov dx, 0
div cx ;此时,ax保存高位运算后产生的商,dx保存高位运算后产生的余数
mov bx, ax ;高位运算后产生的商临时在bx中保存
pop ax ;此时,dx和ax保存了余*10000H+L
div cx
mov cx, dx ;cx中保存最终结果的余数
mov dx, bx ;dx中保存高位运算后产生的商
pop bx
ret
codesg ends
end start
测试1:
计算:1000000/10(F4240H/0AH) 商:100000(186A0H) 余数:0
调试结果:
测试2:
计算:1000020/11(F4254H/0BH) 商:90910(1631EH) 余数:10(0AH)
调试结果: