2015-12-07 62 views
0

在avr程序集中,我想用一個常數來除數。 我檢查瞭如何看avr-gcc如何做到這一點。 所以在C文件中我有:如何在avr-gcc中不斷的工作?

#include <stdint.h> 

uint8_t divide_by_6(uint8_t x) { 
    return x/6; 
} 

,當我運行avr-gcc -O3 -mmcu=atmega16 -D__AVR_ATmega16__ -S main.c它給了我:

divide_by_6: 
    ldi r25,lo8(-85) 
    mul r24,r25 
    mov r24,r1 
    clr r1 
    lsr r24 
    lsr r24 
    ret 

但我不明白這是什麼組件做。 這個彙編代碼如何執行劃分?

+4

準確地說你的問題是什麼? –

+1

這是如何工作的? – orestisf

回答

4

-85是0xFFFFFFFFFFFFFFAB,所以lo8(-85)是是0xAB,它是171。

代碼由171乘以參數,然後返回該產品的最顯著字節,由2右移(即除以4)。

因此它有效地返回X * 171 /(256 * 4)== X *一千○二十四分之一百七十一,這大約是== X * 1/6的== X/6.

3

谷歌:瓊斯上往復乘法

我們都知道或應該從小學知道除以n乘以1/n是一樣的。我們還學會了如何使用小數位。和其他操作,乘以1.234與乘以1234相同,然後除以1000,然後只知道你的結果是1000到大。喜歡用便士代替美元12.34美元就是1234便士。 6小時是360分鐘等。

與二年級相比,二進制的長分區是微不足道的。對於移動的每個數字,可以精確地爲零或恰好一次除數適合拉下來的分子。基本上1/6,你最終得到0.001010101010 ...二進制。

所以,如果我想要1234/6我可以做1234 * 0x2AAA = 0xCDA774。 (1/6)* 65536))/ 65536 = X/6或(X * 0x2AAA),0xCDAAA是(1/6)65536 so(X >> 16大約是X/6。

現在圓呢? 1234/6實際上是205和2/3rds,所以如果你想要收集的話。四捨五入就意味着你在截止後得到這個數字,如果這個數字在你的右半部分或之上?以及10101010截止後的數字是1,1/2是等於或大於一半,爲什麼不使用0x2AAB?

另外我們知道6 = 2 * 3。這兩個數字很容易出現,所以你可以做(​​N/3)/ 2或(N/2)/ 3。並且1/3是0.01010101 ...二進制,其中1/6是0.001010101 ...

因此,這應該都會導致一個粗略的想法,您可以如何乘以除法和他們乘上的0xAB的粗略想法來自。但其他基本的數學身份也可能在那裏。

請注意,您不必在計算器上執行二進制除法,0x10000/6 = 0x2AAA。 X/6 =(X * 0X10000)/(6 * 0X10000)=(X/0X10000)(0X10000/6)=(X(0X10000/6))/ 0X10000。然後,當您嘗試使用此固定點時,您必須考慮準確性/舍入。有時16位是不夠的,你需要24或32或誰知道......取決於除數。