mod AVR 组件中的 (%) - __divmodhi4
mod (%) in AVR assembly - __divmodhi4
我正在尝试在 AVR 程序集中执行 %10。
我创建了一个简单的 c 文件
int main()
{
int k=19;
int j;
j = k%10;
return 0;
}
然后我编译成汇编,给出
ldi r24,lo8(19)
ldi r25,0
std Y+2,r25
std Y+1,r24
ldd r24,Y+1
ldd r25,Y+2
ldi r18,lo8(10)
ldi r19,0
mov r22,r18
mov r23,r19
rcall __divmodhi4
std Y+4,r25
std Y+3,r24
ldi r24,0
ldi r25,0
__divmodhi4
如何工作以及结果存储在哪里?
由于 AVR 没有硬件除法器,AVR-GCC 编译器必须使用复杂的函数来执行此类操作。
__divmodhi4 - 这些功能之一。它将存储在 r25:r24 中的有符号 16 位整数除以 r23:r22 中的另一个有符号 16 位整数。
Returns r23:r22 中的 16 位商和 r25:r24
中的余数
您应该在看到您自己的代码的同一个反汇编中看到 __divmodhi4。
您还可以查看 GCC 库的查找源 for example, here
为了自己理解这个函数是如何工作的,我用c写了一个版本。
(如果你有办法在你的开发机器上单步执行 AVR 汇编器,那么这可能是不必要的)
这里稍微直译一下:
uint16_t udivmodhi4(uint16_t arg1, uint16_t arg2) {
uint16_t rem = 0;
uint8_t i = 16;
uint8_t carry = 0;
uint8_t carry2 = 0;
do {
carry2 = (arg1 & 0x8000) != 0;
arg1 = (arg1 << 1) + carry;
i--;
rem = (rem << 1) + carry2;
carry = arg2 > rem;
if (!carry) {
rem = rem - arg2;
}
}
while (i);
arg1 = (arg1 << 1) + carry;
arg1 = arg1 ^ 0xffff;
// arg1 has the quotient, rem has the remainder
return arg1;
//return rem;
}
这是我清理后的版本:
uint16_t udivmodhi4(uint16_t arg1, uint16_t arg2) {
uint16_t rem = 0;
for (uint8_t i = 0; i < 16; i++) {
rem = (rem << 1) | (arg1 & 0x8000 ? 1 : 0);
arg1 = arg1 << 1;
if (rem >= arg2) {
rem -= arg2;
arg1 |= 1;
}
}
return arg1;
//return rem;
}
如您所见,它循环了 16 次*,在每个循环中,它从 arg1 中取出最高位,将其移至余数的最低位,比较余数 arg2,并将其移回 arg1,减去arg2 如有必要,取余数。
*:请注意,ASM 在开始时将循环变量设置为 17,但在开始循环之前将其递减,因此它循环了 16 次。此外,ASM 版本将位反转回到 arg1,然后在最后翻转它们。代码中像这样的大多数奇怪之处似乎都是为了优化代码大小。
C 代码不会像 ASM 那样优化到那么少的指令,我这样做只是为了学习目的。底线是,这对 16 循环中的任何被除数和除数进行 16 位无符号除法。
我正在尝试在 AVR 程序集中执行 %10。
我创建了一个简单的 c 文件
int main()
{
int k=19;
int j;
j = k%10;
return 0;
}
然后我编译成汇编,给出
ldi r24,lo8(19)
ldi r25,0
std Y+2,r25
std Y+1,r24
ldd r24,Y+1
ldd r25,Y+2
ldi r18,lo8(10)
ldi r19,0
mov r22,r18
mov r23,r19
rcall __divmodhi4
std Y+4,r25
std Y+3,r24
ldi r24,0
ldi r25,0
__divmodhi4
如何工作以及结果存储在哪里?
由于 AVR 没有硬件除法器,AVR-GCC 编译器必须使用复杂的函数来执行此类操作。
__divmodhi4 - 这些功能之一。它将存储在 r25:r24 中的有符号 16 位整数除以 r23:r22 中的另一个有符号 16 位整数。
Returns r23:r22 中的 16 位商和 r25:r24
中的余数您应该在看到您自己的代码的同一个反汇编中看到 __divmodhi4。
您还可以查看 GCC 库的查找源 for example, here
为了自己理解这个函数是如何工作的,我用c写了一个版本。
(如果你有办法在你的开发机器上单步执行 AVR 汇编器,那么这可能是不必要的)
这里稍微直译一下:
uint16_t udivmodhi4(uint16_t arg1, uint16_t arg2) {
uint16_t rem = 0;
uint8_t i = 16;
uint8_t carry = 0;
uint8_t carry2 = 0;
do {
carry2 = (arg1 & 0x8000) != 0;
arg1 = (arg1 << 1) + carry;
i--;
rem = (rem << 1) + carry2;
carry = arg2 > rem;
if (!carry) {
rem = rem - arg2;
}
}
while (i);
arg1 = (arg1 << 1) + carry;
arg1 = arg1 ^ 0xffff;
// arg1 has the quotient, rem has the remainder
return arg1;
//return rem;
}
这是我清理后的版本:
uint16_t udivmodhi4(uint16_t arg1, uint16_t arg2) {
uint16_t rem = 0;
for (uint8_t i = 0; i < 16; i++) {
rem = (rem << 1) | (arg1 & 0x8000 ? 1 : 0);
arg1 = arg1 << 1;
if (rem >= arg2) {
rem -= arg2;
arg1 |= 1;
}
}
return arg1;
//return rem;
}
如您所见,它循环了 16 次*,在每个循环中,它从 arg1 中取出最高位,将其移至余数的最低位,比较余数 arg2,并将其移回 arg1,减去arg2 如有必要,取余数。
*:请注意,ASM 在开始时将循环变量设置为 17,但在开始循环之前将其递减,因此它循环了 16 次。此外,ASM 版本将位反转回到 arg1,然后在最后翻转它们。代码中像这样的大多数奇怪之处似乎都是为了优化代码大小。
C 代码不会像 ASM 那样优化到那么少的指令,我这样做只是为了学习目的。底线是,这对 16 循环中的任何被除数和除数进行 16 位无符号除法。