NASM汇编浮点异常:数组模3余1求和逻辑循环失效排查
NASM汇编循环逻辑问题排查与修复
问题描述
需求:编写NASM汇编程序,检查数组中每个元素除以3的余数是否为1,若是则将该元素加入最终总和。
原代码:
SYS_WRITE equ 4 SYS_EXIT equ 1 STDOUT equ 1 segment .data x: db 3 db 2 db 1 db 3 db 5 y db 3 segment .bss res resb 1 segment .text global _start _start: mov ecx,5 ; number of iterations xor ebx,ebx ; ebx the sum mov edx,x ; addres of the first element xor eax,eax ;in eax will be the number of elements that are satisfying the conditions i wrote in the text ;above petlja: push ecx push edx push eax push ebx mov cl,[y] ; storing 3 in lower 8 bits of cx register mov ax, [edx]; storing element of array in ax div cl cmp ah,1 jnz izlaz pop ebx pop eax pop edx add ebx,[edx] ;adding to the final sum and increasing the number of elements inc eax push edx push eax push ebx izlaz: pop ebx pop eax pop edx pop ecx inc edx ; increasing the address loop petlja add ebx,'0'; adding character '0' for printing mov [res],ebx; moving final sum to mem. loc. res mov eax,SYS_WRITE ; system call for write mov ebx,STDOUT mov ecx, res mov edx, 1 int 80h ; kernel call mov eax, SYS_EXIT int 80h ; kernel call
用户问题:单独编写除法取余并判断的逻辑时运行正常,但将其嵌入循环后程序完全无法正常工作,尝试论坛中的多种解决方案均无效,请求排查并解决该循环逻辑问题。
问题排查
- 栈平衡被破坏:循环开头push了ecx、edx、eax、ebx四个寄存器,但条件满足时的pop/push操作导致栈内元素数量不匹配,后续循环的寄存器值完全混乱,这是程序崩溃的核心原因。
- 除法指令使用错误:
div cl是8位除法,要求被除数为AX且高8位AH必须为0,但原代码直接用mov ax, [edx]读取2字节数据(数组元素是1字节db),既读取了错误的数值,又可能因AH不为0触发除法错误。 - 数组元素读取错误:数组元素是1字节类型,用
ax读取会获取相邻两个字节的数值,导致后续计算完全错误。
修正后的代码
SYS_WRITE equ 4 SYS_EXIT equ 1 STDOUT equ 1 segment .data x: db 3 db 2 db 1 db 3 db 5 divisor db 3 newline db 0xA ; 可选换行符 segment .bss res_buffer resb 2 ; 预留2字节处理两位数总和 segment .text global _start _start: mov ecx, 5 ; 循环次数(数组元素个数) xor ebx, ebx ; ebx存储总和,初始化为0 mov edx, x ; edx指向数组第一个元素 xor eax, eax ; eax统计符合条件的元素个数 petlja: ; 保存循环依赖的寄存器 push ecx push edx ; 读取1字节数组元素到al,清零ah避免除法错误 mov al, [edx] xor ah, ah mov cl, [divisor] ; 除数存入cl div cl ; 8位除法,余数存于ah cmp ah, 1 ; 判断余数是否为1 jnz skip_add ; 不符合则跳过累加 ; 符合条件,累加元素值并计数 add ebx, [edx] inc eax skip_add: ; 恢复寄存器 pop edx pop ecx inc edx ; 指针移向下一个元素 loop petlja ; 自动递减ecx,直到为0终止循环 ; 将总和转换为ASCII字符串(支持0-99) mov ecx, res_buffer mov eax, ebx xor edx, edx mov ebx, 10 div ebx ; eax=商(十位),edx=余数(个位) add dl, '0' mov [ecx+1], dl add al, '0' mov [ecx], al ; 确定输出长度:个位数只输出个位,两位数输出两位 cmp eax, '0' je print_single mov edx, 2 jmp print_start print_single: mov edx, 1 inc ecx ; 指针指向个位字符 print_start: ; 输出结果 mov eax, SYS_WRITE mov ebx, STDOUT int 0x80 ; 可选:输出换行 mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, newline mov edx, 1 int 0x80 ; 退出程序 mov eax, SYS_EXIT int 0x80
修改说明
- 修复栈平衡:仅保存循环中被修改的ecx和edx,跳转后直接恢复,彻底避免栈溢出或寄存器值混乱的问题。
- 正确处理除法:用
al读取1字节数组元素,提前清零ah确保8位除法的正确性,余数判断逻辑正常工作。 - 优化输出逻辑:新增两位数转换逻辑,解决原代码只能输出个位数的限制,适配更大的总和数值。
- 简化代码结构:移除冗余的寄存器保存操作,循环逻辑更清晰,降低出错概率。
内容的提问来源于stack exchange,提问作者Meda
相关产品推荐
相关产品推荐

