繁体   English   中英

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

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

我得到一个 8 位二进制数,并想在 LCD 屏幕上将其表示为十进制数。

因此,例如,如果我得到 01000011 作为输入,即十进制的 67,我首先必须获得 6 的 8 位 ascii 码,然后是 7 的 8 位 ascii 码,并将它们发送到 LCD。

关于如何在 AVR 汇编程序中完成此操作的任何想法?

这是来自@Sebastian 的算法,它计算除以 10 后的商,正确地在[0, 99]的范围内。

typedef unsigned char byte;

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

强制转换为byte是必要的,因为 C 标准要求将任何中间结果提升为至少 16 位的int ,并且这种转换会导致 AVR 等 8 位处理器的低效代码。

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的 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
...

使用更大的乘数,您可以获得更高的精度,因此对于更大的红利,结果将是准确的。

与二进制数相同。 您可以通过这些表达式计算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

特定精度所需的乘数大小看起来有点不规则,我仍然没有弄清楚它是如何工作的,但为了我们的目的,我们需要将[0, 99]中的数字N除以10 ,这是N / 2 / 5 N / 2 <= 49 ,所以n * (1100(2) + 1) / 2^6最多n <= 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

由于h <= 49h * 3 + (h >> 2)适合 8 位,这在我们之前看到的 C 代码中表示。

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

GCC 认为不同的计算方式更好。 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
*/

旧答案

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

但不要问我它是如何工作的。 它是 AVR GCC 的优化 output 翻译了一个 C function,我通过对 x86 的优化 output Clang 进行逆向工程编写的,所以基本上偷走了两个编译器的工作。

来自这个 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 制作了这个。

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

我翻译成 C。

bytepair divmod10(byte x) {
    byte y = (uint16_t)x * 205 >> 11;
    return (bytepair){y, x - y * 10};
}

然后我将其放入 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

看起来 AVR 是一个非常简单的 8 位机器,甚至没有可变移位。 好吧,它仍然可以比 GCC 的内置软件部门更快地完成这项工作。


暂无
暂无

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

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