如何在ARM7上执行模数?

时间:2019-03-01 18:07:07

标签: assembly modulus arm7

在ARM7上执行模数时遇到很多麻烦。

当前,我有以下代码:

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

但这根本不起作用。

从我的同学所做的事情看来,我们应该通过一点点的方式来做到这一点,但是我不知道这将如何工作。

1 个答案:

答案 0 :(得分:0)

确实,您使用的语法不正确。尽管大多数(全部?)ARM汇编程序都支持MOD运算符,但仅当两个操作数均为汇编时常量时,该运算符才起作用。它只是执行汇编时算术和常量表达式折叠。因此,您可以这样做:

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

实际上将被转换为:

mov  r0, #2

因此将值2移到R0寄存器中。

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

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


根据问题中的代码,您似乎正在将R1寄存器的内容添加到R0寄存器中,然后尝试以模2计算R0

假设整数是 unsigned ,这很简单:

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

之所以可行,是因为x % 2与无符号整数等效于x & 1。通常,只要{em> x % n(除数)是2的幂,x & (n - 1)就等于n 。这不仅易于编写,而且还是性能优化,因为按位运算比除法更快。

现在您知道模的乘方为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


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

对于(r0 + r1) % 2,其中r0r1已签名,因此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指令的常量操作数的简单问题。您甚至需要更多的代码,即使以2的幂次幂为模。同样,ask a compiler的操作方法。绝对为ask a compiler,用于对非2的幂进行模运算。


如果要对两个变量进行一般的模运算,则事情要困难得多,因为不能简单地使用位旋转。 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库函数以进行无符号签名,并分派给__aeabi_idivmod库函数以进行有符号的模/除运算。其他编译器将具有自己的库函数。

请勿尝试在汇编中手动编写此类代码。根本不值得付出努力。如有必要,请从C编译器的标准库中提取函数,然后调用它进行繁重的工作。 (您的老师不希望您这样做。)