assembly 如何在环路中使用ARM ADC?

pb3s4cty  于 2022-12-13  发布在  其他
关注(0)|答案(1)|浏览(128)

在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

我试图保存状态的条件标志,但我不知道怎么做。

kse8i1jr

kse8i1jr1#

要保存/恢复进位标志,可以在寄存器中创建一个0/1整数(可能使用adc reg, zeroed_reg, #0?),然后在下一次迭代cmp reg, #1rsbs 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更新NZ,但不更新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-selectcarry-lookahead,但4位BCD数字与SIMD元素,而不是单一位与硬件全加器。
对于二进制bigint来说,这样做并不值得,因为你可以在32或64位的块中工作,并使用进位标志来帮助你,但当原始硬件操作一次只做4位时,也许会有一些收获。

相关问题