You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 23:02:06