汇编语言实验10.2——解决除法溢出问题

364 阅读3分钟

前言

实验内容来源于王爽老师著作的《汇编语言》第3版,P218,折腾了很久,才编码完成,好记性不如烂笔头,顺手记下,算是一个小小的笔记。

实验环境

win10、dosbox、masm5.00

实验内容:解决除法溢出问题

问题描述:前面讲过,div指令可以做除法。当进行8位除法的时候,用al存储结果的商,ah存储结果的余数;进行16位除法的时候,用ax存储结果的商,dx存储结果的余数。可是,现在有一个问题,如果结果的商大于al或者ax所能存储的最大值,那么将如何?

(ps:内容比较多就懒得一一描述了。)

思考

我们可以通过公式:

X/N=int(H/N)65536+[rem(H/N)65536+L]/NX/N=int(H/N)*65536+[rem(H/N)*65536+L]/N

来解决除法溢出问题。
公式说明:
X:被除数,范围:[0,FFFFFFFF]
X:除数,范围:[0,FFFF]
H:X高16位,范围:[0,FFFF]
L:X低16位,范围:[0,FFFF]
int():描述性运算符,取商,比如,int(38/10)=3
rem():描述性运算符,取余数,比如,rem(38/10)=8
以上都是摘自课本内容,但有一个问题是65536换成十六进制就是10000,需要两个寄存器或者mul dword ptr ds:[si]找到65536

一开始是想着通过dw配合mul来做乘法,如下代码块:

data segment
    dw 65536
data ends

code segment
    ...
    mov ax,dx
    mul dword ptr ds:[0]
code ends

但很快就发现不可行,在debug执行到这一步的是,对应的汇编指令是db 66,一看就懵了,没看懂是啥意思。

要不换寄存器试一下?可这么一来好像需要俩寄存器才能存放的下65536,既然搞不清楚就debug一下呗,反正也就浪费一点时间。

子曰:说帅不过三秒就是这种情况,用了俩寄存器,结果还是不行,代码下:

code segment
    ...
    mov dx,1     ;65536高16位
    mov ax,0     ;65536低16位
    mov bx,5
    mul bx
code ends

想来想去,既然正着来不行,那返过来试一下应该可以吧,65536的十六进制不是10000吗?那仔细想一下,10000对应寄存器的值可以看成dx=0001ax=0000,这么一来,不管乘法怎么算,dx的值永远是计算结果值*1,而,(ax)=0

别说,还真凑巧了,可行!!!

代码实现

assume cs:code,ss:stack

stack segment
	db 8 dup(8)
	db 8 dup(8)
stack ends

code segment
start: 	mov ax,stack
        mov ss,ax
        mov sp,10h

        mov dx,21h
        mov ax,0e88eh
        mov cx,6h

        call divdw

        mov ax,4c00h
        int 21h
		
divdw:	push ax		;将被除数低16位入栈		
                        ;如果不想用栈,那么就需要在四个寄存器中来回交换
                        
        ;先计算H/N,除数为16位,被除数为32位
        mov ax,dx	;(ax)=(dx)=被除数高16位
        xor dx,dx	;被除数高16位位0
        div cx		;(ax)=商,(dx)=余数

        ;int(H/N)*65536
        mov bx,ax	;暂存结果高16位
                        ;由于65536的十六进制是10000,所以做乘法的时候ax永远是0,
                        ;(dx)=1=商*1=(ax)

        ;同理,[rem(H/N)*65536+L]/N 等价于 (H%N)*10000+L
        ;由于10000需要两个寄存器来存储(ax)=0000,(dx)=0001
        ;所以(H%N)*10000在寄存器中的值为(ax)=0000,(dx)=H%N*1
        ;同时L的取值范围[0,ffff]
        ;所以,(H%N*65536+L])寄存器的值为(dx)=H%N*1,(ax)=L
        ;(dx)=余数
        pop ax		;弹出除数低16位
        div cx		;[rem(H/N)*65536+L]/N
        mov cx,dx	;将余数送入cx
        mov dx,bx	;将结果高16位送入dx

        ret
		
code ends
end start 

debug试一了下,结果是正确的,这也算是一个取巧的方式。