这道题之所以单独拎出来分析,是因为需要理解的知识比较多。先看看这道题对编写程序的要求:
这道题主要是考查对DIV指令的深入理解和综合运用。先来复习一下原书第169页对DIV指令的描述:
而关于第10章实验10第2题divdw程序的编写,王爽老师给出了一个公式:
并且他在第336页里的附录中给出了这个公式的证明。
只要搞清楚了DIV指在对于除数为16位、被除数为32位的除法原理之后,这道题很多难点就会迎刃而解。
下面,我们来举一个例子,比如我们要计算:1000000 / 10 (即:000F 4240H / 000AH):
按照王爽老师的思路,000F 4240H 作为被除数是个dword型数据,DX保存的是高16位数据000FH,AX保存的是低16位数据4240H,CX保存的是16位数据000AH,这是一个典型的除数为16位、被除数为32位的除法操作。
按照这种操作方式,其除法结果,AX存储着除法操作的商,DX存储着除法操作的余数。但问题在于,如果只进行一次这种除法运算,其结果是186A0H,必然会产生对AX的溢出。
因此,必须采取一种新的算法,简单地说,就是对000F 4240H进行拆分然后进行除法运算,也就是就是“把高位字、低位字,分别除以 CX”。
第一步:用000F 4240H的的高16位000FH 除以 000AH
这时候,首先将000FH的高16位补上0,扩展成32位,也就是成为0000 000FH,然后除以000AH。在这一步的操作中,让DX保存被除数0000 000FH的高16位0000H,让AX保存被除数0000 000FH的低16位000F,然后除以000AH。
其结果,得到000F 4240H的高16位(000FH)的商 0001H、高16位(000FH)的余数 0005H。而商0001H,则保存在AX中,余数 0005H,则保存在DX中。
这一步,就相当于王爽老师提供的公式中:
int(H/N)*65536 和rem(HN)*65536
而将000F 4240H的高16位000FH扩展成32位,然后除以除数000AH,就相当于int(H/N)*65536
而其结果,int(H/N)*65536产生的商 0001H,就保存在AX中;rem(HN)*65536产生的余数0005H,就保存在DX中。
第二步:用 0005 4240H 除以 000AH
为什么要用0005 4240H除以 000AH呢?是因为此时DX已经保存了上一步的除法结果,也就是000F 4240H的高16位(000FH)余数 0005H;而4240H,则是原来的AX保存的000F 4240H的低16位。
这一步,就相当于王爽老师提供的公式中:
rem(HN)*65536+L
000F 4240H的高16位(000FH)余数 0005H,在乘以65536后,再加上000F 4240H的低16位4240H,就得到了0005 4240H。
这里,由于现在的AX已经保存了第一步计算的000F 4240H的高16位(000FH)商0001H,所以,必然会用到一个中间变量(寄存器或栈)来将AX恢复000F 4240H的低16位数据4240H,进行新一轮的除法计算,也就是王爽老师提供的公式中:
[rem(HN)*65536+L]/N
而现在的AX保存着000F 4240H的高16位(000FH)商0001H,所以必须要用另一个中间变量(寄存器或栈)来保存0001H。
这样的计算结果就是:得到了0005 4240H的商 86A0H、余数 0000H。而这时,商86A0H就保存在AX中,符合了王爽老师的要求:(ax) = 结果的低16位;并且余数 0000H就保存在了DX中。
第三步:将000F 4240H的高16位(000FH)除以000AH的商0001H保存在DX中,将DX中余数0000H保存在CX中。
从而实现:(dx) = 结果的高16位,(cx) = 余数。
这种计算方法,可以用小学用的“竖式”来描述:
小结一下:
第一步:用 0000 000FH 除以 000AH,得到高位商 0001H、高位余数 0005H。
第二步:用 0005 4240H 除以 000AH,可得到低位商 86A0H、低位余数 0000。
最后,回到divdw程序编写的要求上:
应用举例:计算 1000000/10(F4240H/0AH)
mov ax,4240H
mov dx,000FH
mov cx,0AH
call divdw
结果:(dx)=0001H,(ax)=86A0H,(cx)=0
在这个初始条件中,AX保存了低16位数据4240H,DX保存了高16位数据000FH,DX与AX联合作为被数除, CX保存了0AH并作为除数,然后要通过call指令来调用divdw,最终实现DX保存结果(商)的高16位0001H,,AX保存结果(商)的低16位86A0H,CX保存余数0。
根据我们前面的思路,贴出divdw程序的完整代码:
================
;计算1000000/10(F4240H/0AH)
;使用公式:X/N = int(H/N)*65536 + [rem(H/N)*65536+L]/N
assume cs:code
code segment
mov ax,4240h
mov dx,000fh
mov cx,0ah
call divdw
mov ax,4c00h
int 21h
divdw: mov bx,ax;将AX中保存了被除数F4240H的低16位数据4240H,保存到BX中,BX作为临时变量。
mov ax,dx;将DX中保存了被除数F4240H的高16位数据000FH,保存到AX中,AX作为除法运算被除数的低16位。
mov dx,0;将DX清零,作为除法运算被除数的高16位。
div cx ;CX作为除数,这时被除数是32位的,即0000000FH,DX是0000H,AX是000FH。运行后,DX存放余数,AX存放商。
push ax ;通过压栈操作,保存AX中的商,即是最后商的高16位。
mov ax,bx ;将临时变量BX中的被除数F4240H的低16位数据4240H,恢复到AX,作为新一轮除法运算中被除数的低16位数据。
div cx ;此时在上一轮除法运算中DX中的余数,作为被除数的高16位,AX中的数据作为被除数的低16位,进行除法运算。
mov cx,dx ;得到的余数保存在DX中,然后通过DX保存到cx
pop dx ;将最后商的高16位弹出栈,保存在DX中,成为最后商的高16位,低16位已经在ax中。
ret
code ends
end
================