Motorola 68k汇编:如何将无符号长整型转换为ASCII字符串
Motorola 68k 32位无符号长整型转ASCII字符串实现方案
针对68k汇编中divu指令仅支持16位除数、无法直接处理32位无符号数大基数除法的问题,以下提供两种可行实现方案:
方法一:基于权值减法的直观实现
该方案无需依赖除法指令,通过预定义10的幂次权值表逐个计算每一位数字,兼容性强(适配68000及以上),逻辑清晰易维护。
实现代码
; 功能:将32位无符号数转换为ASCII字符串 ; 输入:d0 = 待转换的32位无符号数,a0 = 输出字符串起始地址 ; 输出:转换后的ASCII字符串以0结尾,a0指向字符串末尾的0 unsigned_long_to_ascii: lea pow10_table,a1 ; 指向10的幂次权值表(从10^9到10^0) moveq #9,d2 ; 共10个权值,循环计数器 move.b #0,d3 ; 标记位:是否已输出非零数字(跳过前导零) next_digit: move.l (a1)+,d1 ; 取出当前权值 moveq #0,d4 ; 当前位的数字计数器 ; 计算当前数包含多少个当前权值(得到当前位数字) count_loop: cmp.l d1,d0 bcs count_done ; d0 < 权值,停止计数 sub.l d1,d0 ; 减去一个权值 addq.l #1,d4 ; 数字加1 bra count_loop count_done: tst.b d3 bne write_digit ; 已输出过非零数字,直接写入当前位 tst.l d4 beq skip_digit ; 当前位是前导零,跳过 move.b #1,d3 ; 标记:开始输出有效数字 write_digit: add.b #$30,d4 ; 将数字转换为ASCII码(0->$30,9->$39) move.b d4,(a0)+ ; 写入字符串 skip_digit: dbf d2,next_digit ; 处理下一个权值 ; 处理输入为全零的特殊情况 tst.b d3 bne end_convert move.b #$30,(a0)+ ; 输入是0时,输出'0' end_convert: move.b #0,(a0)+ ; 字符串末尾添加结束符0 rts ; 10的幂次权值表(32位) pow10_table: dc.l 1000000000 ; 10^9 dc.l 100000000 ; 10^8 dc.l 10000000 ; 10^7 dc.l 1000000 ; 10^6 dc.l 100000 ; 10^5 dc.l 10000 ; 10^4 dc.l 1000 ; 10^3 dc.l 100 ; 10^2 dc.l 10 ; 10^1 dc.l 1 ; 10^0
代码说明
- 权值表覆盖了32位无符号数的所有可能位权,确保能处理最大数值4294967295。
- 通过循环减法计算当前位数字,完全规避除法指令的限制。
- 利用标记位跳过前导零,同时单独处理输入为0的特殊场景,保证输出正确性。
方法二:基于乘法近似的高效实现
若对转换性能有要求,可通过乘法近似实现32位无符号数除以10,再通过栈收集数字反向输出,适合性能敏感场景。
核心思路
利用$CCCCCD常数(对应1/10的二进制近似值乘以2^32),通过64位乘法计算商,再用原数 - 商*10得到余数;循环将余数压栈,最后弹出栈转换为ASCII实现高位到低位的输出。
实现代码
; 功能:32位无符号数除以10,返回商和余数 ; 输入:d0 = 32位被除数 ; 输出:d0 = 商,d1 = 余数(0-9) div32u_10: move.l d0,d1 ; 保存原被除数用于计算余数 move.l d0,d2 ; 拆分被除数为高低16位 move.l #$CCCCCD,d3 ; 1/10的近似常数:(2^32 + 6)/10 ; 计算64位乘积:被除数 * $CCCCCD move.w d2,d0 ; d0 = 被除数低16位 swap d2 ; d2 = 被除数高16位 moveq #0,d4 mulx.u d3 ; d4:d0 = 低16位 * 常数 move.l d0,d5 move.l d4,d6 move.w d2,d0 moveq #0,d4 mulx.u d3 ; d4:d0 = 高16位 * 常数 lsl.l #16,d0 ; 高16位乘积左移16位 add.l d5,d0 ; 合并低16位乘积的低32位 adc.l d6,d4 ; 合并进位到高32位 ; 右移35位得到商 lsr.l #3,d4 ror.l #1,d0 lsr.l #30,d0 or.l d4,d0 ; d0 = 最终商 ; 计算余数:余数 = 原被除数 - 商*10 mulu #10,d0 sub.l d0,d1 move.l (sp)+,d0 ; 恢复商的正确值 rts ; 功能:32位无符号数转ASCII字符串(高效版) ; 输入:d0 = 待转换的32位无符号数,a0 = 输出字符串地址 unsigned_long_to_ascii_fast: moveq #0,d1 ; 栈中数字计数 move.l sp,d2 ; 保存栈指针 ; 循环除以10,收集余数到栈 collect_digits: jsr div32u_10 ; d0=商,d1=余数 move.w d1,-(sp) ; 余数压栈(当前最低位) addq.l #1,d1 ; 计数加1 tst.l d0 ; 商为0则停止循环 bne collect_digits ; 弹出栈中余数,转换为ASCII写入字符串 write_digits: move.w (sp)+,d0 add.b #$30,d0 move.b d0,(a0)+ subq.l #1,d1 bne write_digits move.b #0,(a0)+ ; 添加字符串结束符 move.l d2,sp ; 恢复栈指针 rts
代码说明
div32u_10通过乘法近似实现32位无符号除法,避开divu的溢出限制。- 先将余数压栈(低位先入栈),再弹出转换为ASCII,自然实现高位到低位的输出顺序。
内容的提问来源于stack exchange,提问作者Steve_I
相关产品推荐
相关产品推荐

