MASM/UASM中DIV指令执行23÷10触发SIGFPE异常问题排查
问题分析与解决方案
你的问题核心是16位无符号除法指令DIV bx的使用规范被忽略,导致触发除法溢出异常(对应Linux下的SIGFPE信号)。
关键原因
DIV bx是16位无符号除法指令,它默认使用DX:AX组成的32位无符号数作为被除数,运算后AX存储商,DX存储余数。如果每次执行DIV bx前没有清零DX,DX中残留的上一次运算的余数会和当前AX组合成远大于预期的32位被除数。当这个被除数除以10的商超过AX的16位最大值(0xFFFF,即65535)时,就会触发除法溢出异常,也就是你遇到的SIGFPE。
结合你的场景:第一次运算后DX=3,若第二次循环未清零DX,被除数会是3*65536 + AX(假设AX此时是2)=196610,除以10商19661,这在AX范围内;但如果你的循环逻辑错误(比如未将AX更新为商,仍用初始值23循环),被除数会是3*65536+23=196631,商19663仍在范围内,但如果DX累积到>=10时,被除数除以10的商就会超过65535,直接触发异常。
修复方案
在每次执行DIV bx之前,必须用XOR DX,DX清零DX寄存器,确保被除数是AX对应的16位值(DX为0,即0:AX)。
正确的数字拆分打印示例代码:
prN PROC push bx ; 保存寄存器现场 push cx mov bx, 10 ; 除数固定为10 xor cx, cx ; CX记录余数个数 loop_div: xor dx, dx ; 关键:清零DX,准备无符号除法 div bx ; DX:AX / 10 → AX=商,DX=余数 push dx ; 将余数压栈(后续逆序打印) inc cx test ax, ax ; 判断商是否为0 jnz loop_div ; 商不为0则继续拆分 loop_print: pop dx add dl, '0' ; 将余数转换为ASCII字符 ; 以下是DOS环境下的打印逻辑,若为Linux可替换为对应系统调用 mov ah, 02h int 21h dec cx jnz loop_print pop cx ; 恢复寄存器 pop bx ret prN ENDP
额外说明
Linux下的SIGFPE信号不仅对应浮点异常,整数除法的溢出、除零错误都会触发该信号,这是容易混淆的点。你排除了除零、溢出、下溢,但实际上你遇到的就是商溢出,只是之前对溢出的判断未考虑DX未清零导致的32位被除数场景。
内容的提问来源于stack exchange,提问作者Maximilian Wittmer
相关产品推荐
相关产品推荐

