如何在ARM7上执行模数?

问题描述 投票:0回答:1

我在ARM7上做模数时遇到了很多麻烦。

目前,我有这个代码:

ADD R0,R0,R1
MOV R0, R0 MOD 2
BX LR

但它根本不起作用。

从我的同学所做的事情来看,我们应该通过一点点的转变来做,但我不明白这是怎么回事。

assembly modulus arm7
1个回答
0
投票

实际上,你的语法是不正确的。虽然大多数(全部?)ARM汇编程序都支持MOD运算符,但它只适用于两个操作数都是汇编时常量的情况。它只是组装时算术和常量表达式折叠。所以,你可以这样做:

mov  r0, #11 MOD 3     ; R0 = 2 = (11 % 3)

这将基本上转化为:

mov  r0, #2

从而将值2移动到R0寄存器中。

这很好,因为它允许您对声明的常量(用于可读性)执行模数,并且还可以编写表达式,以便它们是人类可读的,因此更易于维护。

但是,当您处理寄存器,变量或任何不是汇编时常量的东西时,它不起作用。


根据您在问题中的代码,看起来您正在将R1寄存器的内容添加到R0register,然后尝试计算R0模2。

假设整数是无符号的,就像这样简单:

add  r0, r0, r1     ; R0 = (R0 + R1)
and  r0, r0, #1     ; R0 = (R0 & 1)
bx   lr

这是有效的,因为x % 2相当于无符号整数的x & 1。一般来说,x % n相当于x & (n - 1),只要n(除数)是2的幂。这不仅更容易编写,而且还是性能优化,因为按位操作比分区更快。

现在您已经知道了2的幂的模数模式,您可以轻松地执行(r0 + r1) % 4

add  r0, r0, r1     ; R0 = (R0 + R1)
and  r0, r0, #3     ; R0 = (R0 & 1)
bx   lr

如果你想用一个不是2的幂的常数来模数,那么事情变得更复杂。我不会试图在集会中手工写出来。相反,我会期待see what a compiler would generate。这是你在汇编中执行(r0 + r1) % 3的方式:

add     r0, r0, r1           ; R0 = (R0 + R1)
movw    r3, #43691           ; \ R3 = 0xAAAAAAAB
movt    r3, 43690            ; /
umull   r2, r3, r3, r0       ; R3:R2 = (R3 * R0)  [R3 holds upper and R2 holds lower bits of result]
lsrs    r3, r3, #1           ; R3 = (R3 >> 1)
add     r3, r3, r3, lsl #1   ; R3 = (R3 + R3 * 2)
subs    r0, r0, r3           ; R0 = (R0 - R3)
bx      lr

编译器已生成优化代码以计算整数模数。它没有进行完全除法,而是通过幻数(乘法逆)将其转换为乘法。这是a standard trick from Hacker's Delighta common strength-reduction optimization used by many compilers


到目前为止,我们已经研究了无符号整数类型的模运算。当你想对有符号整数进行模运算时怎么办?那么,您需要考虑符号位(即MSB)。

对于(r0 + r1) % 2r0r1签署,因此r0 + r1产生签署的结果:

adds   r0, r0, r1     ; R0 = (R0 + R1)   <-- note "s" suffix for "signed"
and    r0, r0, #1     ; R0 = (R0 & 1)    <-- same as before for unsigned
it     mi             ; conditionally execute based on sign bit (negative/minus)
rsbmi  r0, r0, #0     ; negate R0 if signed (R0 = abs(R0))
bx     lr

这与我们对无符号模数的代码非常相似,除了IT + RSBMI对条件否定的指令,基于输入值是否为负(换句话说,取绝对值)。

(您只在问题中指定了ARMv7,而不是您要定位的配置文件。如果您的芯片具有“A”(应用程序)配置文件,则可以省略IT指令。但是,否则,您的目标是Thumb-2指令集,不支持非分支指令的条件执行,因此在IT指令之前需要RSBMI。请参阅Conditional Execution in Thumb-2。)

不幸的是,计算(r0 + r1) % 4不是改变AND指令的常量操作数的简单问题。你需要更多的代码,即使对于两个常量的模数也是如此。再次,ask a compiler怎么做。绝对ask a compiler签署两个非权力的模数。


如果你想对两个变量进行一般模数运算,事情要困难得多,因为你不能简单地使用bit-twiddling。 C compilers are going to emit a call to a library function

UnsignedModulo(unsigned int i, unsigned int j, unsigned int m):
    push    {r3, lr}
    add     r0, r0, r1
    mov     r1, r2
    bl      __aeabi_uidivmod
    mov     r0, r1
    pop     {r3, pc}
SignedModulo(int i, int j, int m):
    push    {r3, lr}
    add     r0, r0, r1
    mov     r1, r2
    bl      __aeabi_idivmod
    mov     r0, r1
    pop     {r3, pc}

在这里,GCC派遣到__aeabi_uidivmod库函数用于unsigned,__aeabi_idivmod库函数用于签名模/分。其他编译器将拥有自己的库函数。

不要在程序集中手动编写这种代码。它根本不值得努力。如有必要,从C编译器的标准库中提取函数,并调用它来完成繁重的工作。 (你的老师不希望你这样做。)

© www.soinside.com 2019 - 2024. All rights reserved.