我得到一個 8 位二進制數,并想在 LCD 螢屏上將其表示為十進制數。
因此,例如,如果我得到 01000011 作為輸入,即十進制的 67,我首先必須得到 6 的 8 位 ascii 碼,然后是 7 的 8 位 ascii 碼并將它們發送到 LCD。
關于如何在 AVR 匯編器中完成此操作的任何想法?
uj5u.com熱心網友回復:
這是來自@Sebastian 的演算法,它計算除以 10 后的商,正確地在[0, 99].
typedef unsigned char byte;
byte div10(byte x) {
x >>= 1;
return (byte)((byte)(x * 3) (x >> 2)) >> 4;
}
強制轉換byte是必要的,因為 C 標準要求將任何中間結果提升int為至少 16 位,并且這種轉換會導致 8 位處理器(如 AVR)的低效代碼。
GCC 翻譯為此。
div10:
mov r18,r24
lsr r18
mov r25,r24
andi r25,lo8(-2)
add r25,r18
lsr r24
lsr r24
lsr r24
add r24,r25
swap r24
andi r24,lo8(15)
ret
您始終可以通過 計算余數dividend - quotient * 10。
以下是根據@Sebastian 的評論對上述方法如何作業的解釋。如果您想要一個復雜的數學解釋,請閱讀評論,但這是我可以通過一些基本數學模糊掌握的內容。
基本上,您可以通過乘以除數的倒數來除數。n / 3 = n * 0.33...
要計算 的整數商n / 3,您可以使用從 派生的這些運算式1 / 3 = 0.33..。
n * (3 1) / 10^1 ; n <= 4
n * (33 1) / 10^2 ; n <= 49
n * (333 1) / 10^3 ; n <= 499
n * (3333 1) / 10^4 ; n <= 4999
...
乘數越大,精度越高,因此結果對于更大的紅利將是準確的。
與二進制數相同。您可以通過這些運算式計算 的整數商n / 5,從 的二進制點運算式匯出1 / 5 = 0.001100110011..(2)。
n * (11(2) 1) / 2^4 ; n <= 3
n * (110(2) 1) / 2^5 ; n <= 13
n * (1100(2) 1) / 2^6 ; n <= 63
n * (11001(2) 1) / 2^7 ; n <= 63
n * (110011(2) 1) / 2^8 ; n <= 63
n * (1100110(2) 1) / 2^9 ; n <= 173
n * (11001100(2) 1) / 2^10 ; n <= 1023
一定精度所需的乘數大小看起來有點不規則,我還沒有弄清楚它是如何作業的,但為了我們的目的,我們需要將一個數字除以N,即。,這樣就足夠了。[0, 99]10N / 2 / 5N / 2 <= 49n * (1100(2) 1) / 2^6n <= 63
因此,我們可以轉換N / 10為N / 2 * 13 >> 6。讓h = N / 2. h * 13溢位 8 位,但由于>> 6會在乘法后丟棄一些低位,因此可以事先進行一些移位。
h * 13 >> 6
= h * 12 h >> 6
= h * 6 (h >> 1) >> 5
= h * 3 (h >> 2) >> 4
Since h <= 49, h * 3 (h >> 2) fits in 8 bits, and this is represented in the C code that we've seen before.
byte div10(byte x) {
x >>= 1;
return (byte)((byte)(x * 3) (x >> 2)) >> 4;
}
GCC thinks a different way of calculation is better. The assembly output of GCC can be rewritten in C as follows.
byte div10(byte x) {
return (byte)((x & 0b11111110) (x >> 1) (x >> 3)) >> 4;
}
/*
div10:
mov r18,r24
lsr r18
mov r25,r24
andi r25,lo8(-2)
add r25,r18
lsr r24
lsr r24
lsr r24
add r24,r25
swap r24
andi r24,lo8(15)
ret
*/
old answer
If you are looking for an algorithm to calculate the quotient and remainder when an 8-bit number is divided by 10, in AVR assembler, this code does the trick.
But don't ask me how it works. It is the optimized output of AVR GCC translating a C function that I wrote by reverse-engineering the optimized output of x86 Clang. So I basically stole the work of two compilers.
From this C code.
#include <stdint.h>
typedef uint8_t byte;
typedef struct {
byte _0;
byte _1;
} bytepair;
bytepair divmod10(byte x) {
return (bytepair){x / 10, x % 10};
}
x86 Clang produced this.
imul ecx, edi, 205
shr ecx, 11
lea eax, [rcx rcx]
lea eax, [rax 4*rax]
sub dil, al
movzx eax, dil
shl eax, 8
or eax, ecx
ret
which I translated to C.
bytepair divmod10(byte x) {
byte y = (uint16_t)x * 205 >> 11;
return (bytepair){y, x - y * 10};
}
which then I put into AVR GCC.
mov r20,r24
ldi r25,0
mov r19,r25
mov r18,r24
lsl r18
rol r19
lsl r18
rol r19
add r18,r24
adc r19,r25
lsl r18
rol r19
lsl r18
rol r19
lsl r18
rol r19
add r18,r24
adc r19,r25
mov r25,r19
mov r24,r18
lsl r24
rol r25
lsl r24
rol r25
add r18,r24
adc r19,r25
mov r24,r19
lsr r24
lsr r24
lsr r24
mov r25,r24
swap r25
lsl r25
andi r25,lo8(-32)
sub r25,r24
lsl r25
lsl r25
sub r25,r24
lsl r25
add r25,r20
ret
It seems AVR is a very simple 8-bit machine without even variable shifts. Well, still it will do the job probably faster than GCC's software division built-in.
- AVR GCC output
- test
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/437331.html
上一篇:是什么讓呼叫約定不同?
