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 位无符号除法。