2016-11-30 60 views
2

使用Arduino,我必须在我的计算机科学类Atmel AVR Assembly中编写一个函数,计算汇编中两个8位值的8位平均值。我也不允许使用任何分支指令(但跳过也没问题)。如何计算汇编中两个8位值的8位平均值?

这是我到目前为止有:

.global average 
average: 
    add r24, r22 
    asr r24 
    ret 

我的程序的一部分,我必须计算的69和60的平均,它返回-64而不是64有谁知道我会使这个功能有效吗?任何帮助将非常感激。

+3

避免整数溢出/环绕的平均技巧:http://stackoverflow.com/a/3816471/224132。我在一分钟之内通过搜索'没有溢出的整数平均值'找到了,因为我知道那里*是一个技巧,但是不记得它。它可能适用于有符号2的补码以及未签名,但我没有检查。如果需要,可以将'signed'放入Google搜索字词中。 –

+0

请注意,我链接的答案只适用于未签名,如果你知道他们在什么顺序。最高票数的答案不需要,但需要比ADD和ROR更多的操作。无论如何,这只是表明,当寻找整数技巧时,不要将自己限制在AVR asm。你会在C中找到很多东西,你可以在AVR中自己实现,甚至可以提供给编译器,看看它是如何实现的。例如其中一些是有用的:https://graphics.stanford.edu/~seander/bithacks.html –

回答

10

诀窍是先添加然后rotate-with-carry将9位结果除以2,并将8位结果留在寄存器中。

关于我在评论中链接的问题的两个答案使用:first,second

的AVR实现的是:

add r24, r25  ; 9-bit result in C and r24 
    ror r24   ; rotate-through-carry, like x86's RCR instruction 

这适用于位的符号或无符号的解释,因为我们所做的是丢弃从另外的9位全结果的低位。没有算术与逻辑换档的选择,也没有环绕。

另请注意,通过向-infinity轮换(不像C的整数除法运算符那样截断为零)来划分。所以(1 + -2) >> 1-1


这是足够小,你应该把它放在一个宏,而不是一个函数。在大多数呼叫站点,它可能至少需要2条指令,因此内联可以节省代码大小,即使您可以使用1个字RCALL instruction而不是2个字CALL也是如此。

+0

有趣。所以在x86中我们可以使用'RCL'来达到同样的效果。不幸的是,编译器无法识别这种优化 –

+0

@LưuVĩnhPhúc:是的,我不知道如何用C来表达它,而不是通过将其转换为更大的无符号类型然后使用>> >>。对于比寄存器更宽的类型,可能没有编译器会将其优化回RCL。 –

+0

即使RCL增加1,Intel(Skylake上的增长率为3)也超过1 uop,因此对于较窄的参数,Intel CPU上的ADD + SHR在64位或32位寄存器上更便宜。如果只有一个输入需要额外的指令来进行零扩展,MOVZX(或MOV)/ ADD/SHR通常应该击败ADD + RCL。特别是因为零延伸的MOV可以让您无损破坏。 –