如何用精简位运算实现向零取整到最近0x50倍数的汇编方法
M6800下短补丁实现0x50对齐方案
利用你提到的「80是16的倍数」特性,结合给定的极窄输入范围(0xE000~0xE7CF),完全不需要通用除法、查找表,仅用10条左右的基础汇编指令即可实现,比现有32对齐的逻辑多8字节左右的补丁量,非常适合热修补丁。
核心思路
- 80(
0x50)是16的倍数,最终对齐结果的低4位必然全0,第一步直接对低字节执行AND #0xF0清低4位,省去低4位的计算 - 高字节每加1,对应地址偏移256字节,而
256 mod 80 = 16,也就是说高字节每变化1,整个地址相对于80倍数的偏移量固定加16,刚好对应低字节高4位加1。输入范围高字节仅0xE0~0xE7共8种取值,其mod5的偏移是固定循环的,不需要动态计算 - 80倍数的低字节仅有
0x00/0x50/0xA0/0xF0四个固定值,只需要两次比较即可将处理后的低字节修正到对应值,必要时处理高字节借位即可
参考实现(M6800汇编)
假设16位输入值高字节存在0x00内存单元、低字节存在0x01单元(如果存在索引寄存器X中,只需要改成对应取指序列即可,长度基本一致):
; 取高字节计算固定偏移,H&0b11即可索引4个循环偏移值 LDAA $00 ; 加载高字节到累加器A ANDA #$03 ; 取高字节低2位,匹配偏移循环周期 TAB ; 暂存偏移索引到B ; 偏移直接通过分支加载立即数,无需单独建表 TBA BEQ off_10 ; 索引0 → 偏移0x10 CMPA #$01 BEQ off_40 ; 索引1 → 偏移0x40 CMPA #$02 BEQ off_30 ; 索引2 → 偏移0x30 LDAA #$20 ; 索引3 → 偏移0x20 BRA load_low off_10: LDAA #$10 BRA load_low off_40: LDAA #$40 BRA load_low off_30: LDAA #$30 load_low: ADDA $01 ; 偏移加原始低字节 ANDA #$F0 ; 清低4位,得到16对齐的中间值 ; 修正到最近的向下0x50对齐低字节 CMPA #$50 BCS set_zero ; 小于0x50 → 低字节设0,高字节借位 CMPA #$A0 BCS set_50 ; 0x50~0x9F → 低字节设0x50 CMPA #$F0 BCS set_A0 ; 0xA0~0xEF → 低字节设0xA0 LDAA #$F0 ; 大于等于0xF0 → 低字节设0xF0 BRA store_res set_50: LDAA #$50 BRA store_res set_A0: LDAA #$A0 BRA store_res set_zero: CLRA DEC $00 ; 低字节为0时向高字节借位 store_res: STAA $01 ; 存回低字节,计算完成
优化说明
如果输入值默认放在X索引寄存器中,可以省掉内存读写指令,总长度能压到12字节以内,比通用除法实现短70%以上。如果确认输入范围不会低于0xE040,可以去掉高字节借位的DEC $00指令,代码长度还能再缩1字节。如果允许更极限的优化,还可以把偏移计算和比较分支合并,再省2~3条指令。
内容的提问来源于stack exchange,提问作者Ben Zotto
相关产品推荐
相关产品推荐

