ARM汇编实现BCD逐位带进位加法及结果存储与溢出判断
嘿,我来帮你解决BCD码逐位带进位相加的问题,包括ARM Thumb汇编的实现细节!
一、BCD码逐位带进位相加的核心逻辑
BCD码是用4位二进制表示0-9的十进制数,直接做二进制加法会出现无效值(10-15,即0xA-0xF),必须修正才能得到正确的BCD结果。具体步骤如下:
- 对每一组4位(一个BCD数位)执行带进位的二进制加法:
当前位和 = 数1的当前位 + 数2的当前位 + 进位 - 判断是否需要修正:如果当前位和的低4位>9,或者加法过程中产生了进位(即当前位和≥16),就给当前位和加6(0x06)。这是因为6的二进制是0110,加上后会把无效的10-15“推”到下一位进位,同时低位变成正确的BCD值(比如0xA+6=0x10,对应BCD的10,低位0,进位1)
- 把修正后的当前位存入结果的对应位置,更新进位标志,继续处理下一位,直到所有位处理完成
- 最后如果最高位处理完还有进位,说明结果溢出了(比如两个8位BCD数相加超过99999999)
二、ARM Thumb汇编实现
根据你的需求,我们实现一个函数:接收两个BCD数、结果存储地址,把结果写入RAM,返回结果地址和溢出标志(0无溢出,1溢出)。以下是完整代码:
AREA mydata, DATA, READWRITE SUM DCD 0 ; 示例结果存储位置,也可以传入自定义地址 AREA mycode, CODE, READONLY THUMB EXPORT __main EXPORT bcd_add ; 导出BCD加法函数 NUM1 DCD 0x275 ; 示例BCD数:275 NUM2 DCD 0x189 ; 示例BCD数:189,相加结果应为0x464(464) __main ; 调用bcd_add函数:R0=待加数1, R1=待加数2, R2=结果存储地址 LDR R0, =NUM1 LDR R0, [R0] LDR R1, =NUM2 LDR R1, [R1] LDR R2, =SUM BL bcd_add ; 此时R0=结果地址,R1=溢出标志 ; 可以在这里添加调试输出或其他逻辑 LOOP B LOOP ; 死循环保持程序运行 ; BCD加法函数 ; 输入:R0=待加数1(BCD格式), R1=待加数2(BCD格式), R2=结果存储地址 ; 输出:R0=结果存储地址, R1=溢出标志(0=无溢出,1=溢出) bcd_add PUSH {R3-R7, LR} ; 保存寄存器现场 MOV R3, #0 ; R3存储最终结果 MOV R4, #8 ; 循环计数器:32位共8个BCD位(4位/位) MOVS R5, #0 ; 初始化进位标志C为0(MOVS会更新C) bcd_loop ; 取出两个数的当前低4位 AND R6, R0, #0x0F AND R7, R1, #0x0F ; 带进位相加:R6 = R6 + R7 + C ADCS R6, R6, R7 ; 判断是否需要修正:低4位>9 或 有进位 MOV R5, R6 AND R5, R5, #0x0F CMP R5, #9 BHI bcd_correct ; 如果低4位>9,跳转到修正 BCS bcd_correct ; 如果有进位(C=1),跳转到修正 B bcd_store ; 不需要修正,直接存储 bcd_correct ; 加6修正,同时更新进位 ADDCS R6, R6, #0x06 bcd_store ; 将当前位放到结果的对应位置(左移对应位数) MOV R5, #4 MUL R5, R5, R4 SUB R5, R5, #4 LSL R6, R6, R5 ORR R3, R3, R6 ; 右移两个数,处理下一位 LSR R0, R0, #4 LSR R1, R1, #4 ; 计数器减1,循环 SUBS R4, R4, #1 BNE bcd_loop bcd_end ; 存储结果到RAM STR R3, [R2] ; 设置溢出标志:如果最终进位C=1,R1=1;否则0 MOV R1, #0 BCC bcd_no_overflow MOV R1, #1 bcd_no_overflow ; 返回结果地址 MOV R0, R2 POP {R3-R7, PC} ; 恢复现场并返回
代码说明
- 寄存器使用:用R3暂存结果,R4做循环计数器,R6/R7处理当前BCD位,R5辅助计算位移和判断
- 修正逻辑:通过
BHI(高于9)和BCS(有进位)判断是否需要加6,确保每一位都是有效的BCD值 - 溢出判断:循环结束后检查进位标志C,若C=1说明最高位相加后仍有进位,结果超出32位BCD范围(最大99999999),返回溢出标志1
- 调用约定:符合Thumb指令集的寄存器使用规范,保存了非volatile寄存器(R3-R7),返回值用R0(结果地址)和R1(溢出标志)
你可以修改NUM1和NUM2的值测试不同情况,比如NUM1=0x9999、NUM2=0x0002,结果会是0x00010001,溢出标志为1,符合预期。
内容的提问来源于stack exchange,提问作者sono2424
相关产品推荐
相关产品推荐

