简体   繁体   English

如何从二进制数中获取每个数字的ascii

[英]How to get ascii for each digit from a binary number

I am getting an 8 bit binary number and want to represent that as a decimal number on a LCD screen.我得到一个 8 位二进制数,并想在 LCD 屏幕上将其表示为十进制数。

So for example if I get 01000011 as input, which is 67 in decimal I first have to get the 8 bit ascii code for 6, then the 8 bit ascii code for 7 and send those to the LCD.因此,例如,如果我得到 01000011 作为输入,即十进制的 67,我首先必须获得 6 的 8 位 ascii 码,然后是 7 的 8 位 ascii 码,并将它们发送到 LCD。

Any idea on how this could be done in AVR Assembler?关于如何在 AVR 汇编程序中完成此操作的任何想法?

This is an algorithm from @Sebastian which computes the quotient after dividing by 10, correctly in the range of [0, 99] .这是来自@Sebastian 的算法,它计算除以 10 后的商,正确地在[0, 99]的范围内。

typedef unsigned char byte;

byte div10(byte x) {
    x >>= 1;
    return (byte)((byte)(x * 3) + (x >> 2)) >> 4;
}

The casts to byte is necessary because the C standard requires to promote any intermediate result to an int which is at least 16-bit, and such conversion results to inefficient code for 8-bit processors like AVR.强制转换为byte是必要的,因为 C 标准要求将任何中间结果提升为至少 16 位的int ,并且这种转换会导致 AVR 等 8 位处理器的低效代码。

GCC translates to this. 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

You can always calculate the remainder by dividend - quotient * 10 .您始终可以通过dividend - quotient * 10计算余数。


Here is an explanation on how the above method works based on @Sebastian's comments.这是基于@Sebastian 的评论对上述方法如何工作的解释。 Read the comments if you'd like a mathematically sophisticated explanation, but this is what I can vaguely grasp with some basic math.如果您想要一个数学上复杂的解释,请阅读评论,但这是我可以通过一些基本数学模糊地掌握的内容。

Basically, you can divide a number by multiplying the divisor's inverse.基本上,您可以通过乘以除数的倒数来除一个数。 n / 3 = n * 0.33.. . n / 3 = n * 0.33..

To calculate the integer quotient of n / 3 , you can use these expressions, derived from 1 / 3 = 0.33.. .要计算n / 3的 integer 商,您可以使用从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
...

With a larger multiplier, you get higher precision, so the result will be accurate for a bigger dividend.使用更大的乘数,您可以获得更高的精度,因此对于更大的红利,结果将是准确的。

Same with binary numbers.与二进制数相同。 You can calculate the integer quotient of n / 5 by these expressions, derived from the binary point expression of 1 / 5 = 0.001100110011..(2) .您可以通过这些表达式计算n / 5的 integer 商,这些表达式源自二进制小数点表达式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

The required size of the multiplier for a certain precision looks kind of irregular, and I still haven't figured out how it works, but for our purpose, we need to divide a number N in [0, 99] by 10 , which is N / 2 / 5 .特定精度所需的乘数大小看起来有点不规则,我仍然没有弄清楚它是如何工作的,但为了我们的目的,我们需要将[0, 99]中的数字N除以10 ,这是N / 2 / 5 N / 2 <= 49 , so n * (1100(2) + 1) / 2^6 which works up to n <= 63 suffices. N / 2 <= 49 ,所以n * (1100(2) + 1) / 2^6最多n <= 63就足够了。

We can thus transform N / 10 to N / 2 * 13 >> 6 .因此,我们可以将N / 10转换为N / 2 * 13 >> 6 Let h = N / 2 .h = N / 2 h * 13 overflows in 8 bits, but since >> 6 will discard some of the lower bits after the multiplication, it's okay to do some shifts beforehand. 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.由于h <= 49h * 3 + (h >> 2)适合 8 位,这在我们之前看到的 C 代码中表示。

byte div10(byte x) {
    x >>= 1;
    return (byte)((byte)(x * 3) + (x >> 2)) >> 4;
}

GCC thinks a different way of calculation is better. GCC 认为不同的计算方式更好。 The assembly output of GCC can be rewritten in C as follows. GCC的汇编output可以改写成C如下。

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.如果您正在寻找一种算法来计算 8 位数字除以 10 时的商和余数,在 AVR 汇编程序中,这段代码可以解决问题。

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.它是 AVR GCC 的优化 output 翻译了一个 C function,我通过对 x86 的优化 output Clang 进行逆向工程编写的,所以基本上偷走了两个编译器的工作。

From this C code.来自这个 C 代码。

#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. x86 Clang 制作了这个。

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.我翻译成 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.然后我将其放入 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.看起来 AVR 是一个非常简单的 8 位机器,甚至没有可变移位。 Well, still it will do the job probably faster than GCC's software division built-in.好吧,它仍然可以比 GCC 的内置软件部门更快地完成这项工作。


声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM