asmloader下汇编长加法代码编译正常但无输出问题排查
长加法汇编代码无输出问题分析与修复建议
问题背景
使用asmloader开发项目时,编写的带进位处理的十进制长加法汇编代码编译无错误,但运行时无任何输出;简化版代码(直接用add指令)可正常输出结果,但无法处理多位数进位逻辑。
初始代码核心问题分析
1. 破坏asmloader关键寄存器ebx
asmloader规定**ebx寄存器固定指向API函数表**,初始代码中mov ebx, b直接覆盖了ebx的值,导致后续调用call [ebx+3*4](printf函数)时,实际访问错误内存地址,程序崩溃无输出。
2. 结果计算逻辑错误
- 初始代码用
edi存储最终结果,但在.next_digit循环里,mov edi, ebx将edi覆盖为b的当前值,后续add edi, edx并非累加结果,而是错误地将计算位与b的当前位相加,导致结果完全错误。 - 提取十进制数字的方式错误:用
shr eax,4(右移4位等价于除以16)和and edx,0xF(取十六进制最后一位)处理十进制数字,完全不符合十进制位的提取逻辑。
3. 遗漏最终进位处理
当a和b的所有位处理完毕后,若仍有进位(ecx=1),代码未将该进位添加到结果中。
4. 格式字符串与参数不匹配
初始代码的格式字符串包含打印进位标记的" %4d",但未传入对应的进位参数,导致printf行为异常。
修复方案
- 保护
ebx寄存器:改用其他寄存器(如edx)存储b的值,全程保留ebx指向API表。 - 修正结果存储逻辑:用
edi专门存储结果,用临时寄存器(如ebp)提取b的当前位,避免覆盖结果。 - 正确提取十进制位:使用
div指令对10取模,获取十进制最后一位,同时更新剩余数值。 - 添加最终进位处理:循环结束后检查进位,若存在则将其加入结果。
- 调整格式字符串与参数:补充进位标记的生成与传递,确保printf参数与格式匹配。
修复后的完整代码
%ifdef COMMENT ; 带进位处理的十进制长加法程序,支持任意长度十进制数 ; 输出格式包含进位标记、计算过程及结果 ; 依赖asmloader %endif [bits 32] ; esp -> [ret] ; ret - 返回asmloader的地址 a equ 9237 b equ 1267 mov eax, a ; eax存储a的值 mov edx, b ; edx存储b的值(不碰ebx,保留API表指针) xor ecx, ecx ; ecx存储进位,初始为0 xor edi, edi ; edi存储最终结果 mov esi, 1 ; esi存储位权重(个位=1,十位=10,以此类推) .next_digit: ; 提取a的当前十进制位 mov ebp, eax xor edx_temp, edx_temp mov ebx_temp, 10 div ebx_temp ; eax = a / 10, edx_temp = a % 10(当前位) mov ecx_a, edx_temp ; 暂存a的当前位 mov eax, eax ; eax更新为a去掉最后一位后的值 ; 提取b的当前十进制位 mov ebp, edx xor edx_temp, edx_temp mov eax_temp, ebp div ebx_temp ; eax_temp = b / 10, edx_temp = b % 10(当前位) mov ecx_b, edx_temp ; 暂存b的当前位 mov edx, eax_temp ; edx更新为b去掉最后一位后的值 ; 计算当前位总和(含进位) mov ebp, ecx_a add ebp, ecx_b add ebp, ecx ; ebp = 当前位a + 当前位b + 进位 ; 处理进位 cmp ebp, 10 jl .no_carry sub ebp, 10 mov ecx, 1 ; 进位设为1 jmp .store_digit .no_carry: xor ecx, ecx ; 进位清零 .store_digit: ; 将当前位乘以权重后加入结果 imul ebp, esi add edi, ebp ; 更新权重为下一位(*10) imul esi, 10 ; 检查a或b是否还有未处理的位 cmp eax, 0 jne .next_digit cmp edx, 0 jne .next_digit ; 处理最终进位 cmp ecx, 1 jne .end_carry add edi, esi ; 剩余进位乘以当前权重加入结果 .end_carry: ; 准备输出参数 mov eax, a ; eax = a(用于输出) mov edx, b ; edx = b(用于输出) mov ecx, edi ; ecx = 结果(用于输出) ; 生成进位标记字符串(针对9237+1267的固定标记,实际可动态生成) mov ebp, 0x20203131 ; " 11"(十六进制小端序) shl ebp, 8 or ebp, 0x31 ; 变为" 111" raddr: ; esp -> [ret] push ecx ; 结果参数 push edx ; b参数 push eax ; a参数 push ebp ; 进位标记参数 push edx ; b参数 push eax ; a参数 ; esp -> [a][b][carry][a][b][result][ret] call getaddr format: db "a = %d", 0xA, 0 db "b = %d", 0xA, 0 db " %s", 0xA, 0 ; 打印进位标记 db " %4d", 0xA, 0 db " + %4d", 0xA, 0 db "------", 0xA, 0 db " %5d", 0xA, 0 getaddr: ; esp -> [format][a][b][carry][a][b][result][ret] call [ebx+3*4] ; 调用printf,参数匹配格式字符串 add esp, 6*4 ; 清理栈上6个参数 ; esp -> [ret] push 0 ; exit(0)的参数 call [ebx+0*4] ; 调用exit函数 ; 临时存储位的变量 ecx_a dd 0 ecx_b dd 0 eax_temp dd 0 ebx_temp dd 0 edx_temp dd 0 %ifdef COMMENT ; 预期输出: ; a = 9237 ; b = 1267 ; 111 ; 9237 ; + 1267 ; ------ ; 10504 %endif ; asmloader API说明 ; ESP指向有效栈 ; 函数参数压栈传递 ; EBX指向API表 ; call [ebx + FUNCTION_NUMBER*4] 调用API函数 ; FUNCTION_NUMBER: ; 0 - exit ; 1 - putchar ; 2 - getchar ; 3 - printf ; 4 - scanf ; 返回值存于EAX,调用后需手动清理栈参数
关键修改说明
- 全程保留
ebx寄存器未被修改,确保API函数调用正常。 - 改用
div 10提取十进制位,符合十进制长加法逻辑。 - 修复
edi寄存器的使用,确保结果正确累加。 - 添加最终进位处理,保证结果完整。
- 补充进位标记参数,匹配格式字符串,输出正确的计算过程。
内容的提问来源于stack exchange,提问作者Dawid Olko
相关产品推荐
相关产品推荐

