在arm汇编语言中,指令ADCS
将与条件标志C
相加并设置条件标志。而CMP
指令做同样的事情,因此条件标志将被恢复。我该如何解决它?这是我的代码,它正在与r0和r1进行BCD加法:
ldr r8, =#0
ldr r9, =#15
adds r7, r8, #0
ADDLOOP:
and r4, r0, r9
and r5, r1, r9
adcs r6, r4, r5
orr r7, r6, r7
add r8, r8, #1
mov r9, r9, lsl #4
cmp r8, #3
bgt ADDEND
bl ADDLOOP
ADDEND:
mov r0, r7
我试图保存状态的条件标志,但我不知道怎么做。
1条答案
按热度按时间kse8i1jr1#
要保存/恢复进位标志,可以在寄存器中创建一个0/1整数(可能使用
adc reg, zeroed_reg, #0
?),然后在下一次迭代cmp reg, #1
或rsbs reg, reg, #1
中设置进位标志。ARM不能在没有任何设置的情况下用单个指令将C具体化为整数0/1;编译器通常在不在循环(Godbolt)中时使用
movcs r0, #1
/movcc r0, #0
,但在循环中,您可能希望在循环外将寄存器清零一次,而不是使用基于进位设置/进位清零的两条指令。循环而不修改C
使用
teq r8, #4
/bne ADDLOOP
作为循环分支,类似于do{}while(r8 != 4)
的底部。或者使用
tst r8,r8
/bne ADDLOOP
从4开始倒计时,使用sub r8, #1
代替add。TEQ
更新N
与Z
,但不更新C或V标志.(除非使用移位得源操作数,否则它可以更新C).docs-与cmp
不同,它设置eors
之类得标志.eq
/ne
条件得作用相同:当输入相等时,减法和XOR都产生0,在其他情况下产生非0。但是teq
甚至不设置C或V标志,而且无论如何,大于/小于都没有意义。这就是优化的BigInt代码(如GMP)所做的,例如在其
mpn_add_n
函数(source)中,添加了两个bigint输入(32位块的数组)。IDK为什么要跳过
bl
(分支-链接),它把lr设置为返回地址。不要这样做,把asm循环结构成do{}while()
because it's more efficient,特别是当trip-count已知为非零时,这样在某些情况下你就不必担心循环运行零次。有
cbz
/cbnz
指令(docs)可以跳转到寄存器为零或非零的位置而不影响标志,但它们只能向前跳转(跳出循环,越过无条件分支)。它们也只能在Thumb模式下使用,这与teq
不同,teq
可能是专门给予ARM编写BigInt循环而设计的。BCD码添加
你的算法有缺陷;你需要十进制进位,比如
0x05 + 0x06
=0x11
,而不是压缩BCD中的0x0b
。甚至二进制进位标志也不是由
0x0005000 + 0x0007000
之类的东西设置的;高位没有进位输出,只有到下一个半字节的进位。同样,adc
在寄存器的底部添加进位输入,而不是在半字节你的屏蔽隔离。因此,您可能需要从和中减去
0x000a000
(例如移位),因为这样会进位(ARM在减法运算中将C设置为!borrow,因此可能需要对rsb
进行反减或交换操作数)。neon 应该能够解压缩为8位元素(屏蔽奇/偶和交织),并并行处理所有半字节,但进位传播是个问题; ARM没有有效的方式在SIMD向量条件下进行分支(与x86
pmovmskb
不同)。仅对向量进行字节移位和加法就可能生成更多进位,与999999 + 1
一样。IDK,如果这可以有效地减少与硬件使用相同的技术,如carry-select或carry-lookahead,但4位BCD数字与SIMD元素,而不是单一位与硬件全加器。
对于二进制bigint来说,这样做并不值得,因为你可以在32或64位的块中工作,并使用进位标志来帮助你,但当原始硬件操作一次只做4位时,也许会有一些收获。