王爽老师的《汇编语言》在实验10.2中提出了div
指令可能出现的除法溢出的问题。例如对于16位除以8位的情形,考虑被除数是0x1234,除数是0x01。根据div
的约定,商在AL中,余数在AH中。但是显然计算结果的商是0x1234,8位的AL无法容纳,因此会产生除法溢出。使用Bochs调试时,会在div
之后出现iret
,即CPU产生了一个中断返回指令。
为了兼容32位对16位的除法,设计一个双字除法调用过程divdw:
- 输入:
- 被除数:一个32位的整数,高16位在DX中,低16位在AX中
- 除数:一个16位的整数,存储在CX中
- 输出:
- 商:32位的整数,高16位在DX中,低16位在AX中
- 余数:16位整数,储存在CX中
因为商也是双字长,因此divdw过程一定不会发生除法溢出。这是因为,考虑被除数最大是0xFFFFFFFF,除数最小是0x0001,则商最大为0xFFFFFFFF,不会超过双字长。但是直接使用div
指令是无法办到的,所以我们下面需要设计divdw的过程。首先给出一个数论中关于带余数除法的引理:
(带余数除法):设
是两个整数,
,则存在唯一的整数对
,使得
我们接下来约定,符号表示实数除法,
表示整数除法的商,相当于上面的
;
表示整数除法的余数,相当于上面的
。例如,对于
,我们有
现在来考虑双字除法。设被除数如下:如同我们可以把10进制数
写成
,16进制下的数
可以写为
显然
和
分别是寄存器
DX
和AX
中的值。
现在设除数是,从而
再次强调此时我们表示的是实数除法。根据整数带余数除法引理,对于
,又存在唯一的如下表示
其中
,从而
,于是
因为
是16位的,因此
除以
的整数部分
必然也不超过16位。
对于括号中的部分,讨论其取值范围(设是正整数)
因为
是16位整数,故
,于是
由此可知,对其取整的结果:
不超过16位。
现在,令我们重新叙述结果:
换句话说
显然
是整数,并且
。这是因为通过定义可知,
是实数
减去它的整数部分(即
),因此结果就是该实数的小数部分,因此
,从而
,根据带余数除法引理,这个余数
是唯一的。
因此我们有
因为
均不超过16位,因此除法结果,商的高16位
存储在
DX
中,低16位存储在
AX
中,余数不能大于除数
,故而也可以安全存储在
CX
中。