在ARM7上执行模数时遇到很多麻烦。
当前,我有以下代码:
ADD R0,R0,R1
MOV R0, R0 MOD 2
BX LR
但这根本不起作用。
从我的同学所做的事情看来,我们应该通过一点点的方式来做到这一点,但是我不知道这将如何工作。
答案 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 Delight和a common strength-reduction optimization used by many compilers。
到目前为止,我们已经研究了对 unsigned 整数类型的模运算。当您想对有符号的整数进行模运算时该怎么办?好吧,您需要考虑符号位(即MSB)。
对于(r0 + r1) % 2
,其中r0
和r1
已签名,因此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编译器的标准库中提取函数,然后调用它进行繁重的工作。 (您的老师不希望您这样做。)