shamork 发表于 2009-11-27 20:12:22

改造好方便keilC调用的最快16位2进制转压缩BCD 51库(关键字bin2BCD HEX int)

Hex2BCD的算法在http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3587651&bbs_page_no=1讨论时cowboy提到了16位2进制转压缩BCD的最快算法, 汇编的。当时没看懂,所以也没仔细研究。前两天翻出来看,突然来了精神,想改造下为不大用汇编的人民造福(改造下方便用C调用),下面就是成果了。是用C编译成src文件后改的。用了全局变量。本想用局部的,搞不好。不会传参数。
下面是完整的KeilC工程。
点击此处下载 bin2bcd0.1.rar(文件大小:19K) (原文件名:bin2bcd0.1.rar)

下面是cowboy贴出的DENGM的源代码。经我改造,在你的函数中加入bin2bcd.h,把要转换的uint放到IntBinBuf,调用Bin2BCD(),就可以在CharBCDBuf中得到压缩BCD了。
输入:全局变量 unsigned int IntBinBuf;要转换的16位整数,<65536
输出:全局变量 unsigned char CharBCDBuf;        存放转换好的压缩BCD码
如果IntBinBuf=65535,则CharBCDBuf=06 CharBCDBuf=55 CharBCDBuf=35
改造后的bin2bcd.asm中包含reg51.h的内容,不同的你可以改,直接复制reg52.h等的内容替换就行了。
工程调试运行可以在Keil的串口输出中看到。

=====================================
号称世上最快的16bit二进制数转5位压缩BCD码的程序,最长56周期(标准51)。原理还没弄懂。
原作者:DENGM
;R2R3-->R5R6R7 小端
BIN2BCD:
       MOVA, R3      
       ANLA, #0FCH
       RR   A
       RR   A
       MOVR5,A
       ADDA, R5
       ADDA, R5
       MOVR7,A
       MOVA, R2
       ANLA, #3
       MOVR6,A
       XRLA, R3
       ANLA, #3
       XRLA, R2
       RR   A
       RR   A
       ADDA, R7
       JNCL2
       INCR5
       ADDA, #6
L2:    ADDA, R7
       MOVB, #25
       JNCL3
       INCR5
       ADDA, #6
       DIVAB
       SJMP L4
L3:    DIVAB
       CJNE A, #10, L4
       INCR5
       CLRA
L4:    MOVR7,A
       MOVA, #10
       XCHA, B
       ADDA, #(L5-$-3)
       MOVC A, @A+PC
       ADDA, R6
       DA   A
       XCHA, R5
       DIVAB
       XCHA, R7
       SWAP A
       ORLA, B
       SWAP A
       MOVR6,A
       RET
      
L5:    DB 00H, 04H, 08H, 12H, 16H
       DB 20H, 24H, 28H, 32H, 36H
       DB 40H, 44H, 48H, 52H, 56H
       DB 60H, 64H, 68H, 72H, 76H
       DB 80H, 84H, 88H, 92H, 96H
       END

sosingle 发表于 2009-11-28 10:02:43

mark

killin 发表于 2009-11-28 10:18:07

支持楼主,我把原程序也简单的注释一下


BIN2BCD:
       MOVA, R3      
       ANLA, #0FCH;取高字节的高6位
       RR   A
       RR   A         ;除以4
       MOVR5,A      ;有多少个1024,放入R5,
       ADDA, R5
       ADDA, R5   ;乘以3
       MOVR7,A      ;1024的个数乘以3,放入R7
       MOVA, R2
       ANLA, #3   ;取低字节的低2位,高6位为0
       MOVR6,A      ;低字节的低两位放入R6
       XRLA, R3   ;高字节与低字节低2位想异或,高6位为高字节的
       ANLA, #3   ;高字节与低字节低2位想异或,高6位为0
       XRLA, R2   ;低2位为高字节的低2位,高6位为低字节的高6位
       RR   A         ;
       RR   A         ;两字节中从高字节低2位到低字节高6位的组成的一个字节
       ADDA, R7   ;下面的步骤求出有多少个4,由于1024比1000多出24,1024的个数乘以6
       JNCL2      ;也就可以求出这么多个1024中含有的4的个数,它可能大于256,所以分两次加
       INCR5      ;逢256个4,超过1000,1000个数加1
       ADDA, #6   ;由于250X4=1000,多出了6个,4的个数要加上6
L2:    ADDA, R7   ;A中存放的是4的个数,
       MOVB, #25
       JNCL3
       INCR5      ;
       ADDA, #6   ;与上次加法运算的原理相同
       DIVAB
       SJMP L4
L3:    DIVAB            ;至此A中存放的是4的个数,除以25,也就可以得出100的个数
       CJNE A, #10, L4
       INCR5            ;如果超过25X10个4,R5需加1;至此R5中存放的是1000的个数
       CLRA             ;剩余的个数不超过25
L4:    MOVR7,A          ;100的个数入R7
       MOVA, #10
       XCHA, B          ;余数入A,前面求出1000和100的个数,剩余的4的个数用于求十位和个位。
       ADDA, #(L5-$-3)
       MOVC A, @A+PC      ;依据余数取数
       ADDA, R6         ;加上低两位
       DA   A             ;十进制调整
       XCHA, R5         ;十位个位放入R5,A中的是1000的个数
       DIVAB            ;除以10,商也就是10000的个数,余数为1000的个数
       XCHA, R7         ;10000的个数放入R7,A中为100的个数
       SWAP A             ;100个数放高半字节
       ORLA, B          ;1000的个数低半字节
       SWAP A             ;交换
       MOVR6,A          ;放入R6
       RET
   
L5:    DB 00H, 04H, 08H, 12H, 16H
       DB 20H, 24H, 28H, 32H, 36H
       DB 40H, 44H, 48H, 52H, 56H
       DB 60H, 64H, 68H, 72H, 76H
       DB 80H, 84H, 88H, 92H, 96H
       END

shamork 发表于 2009-11-29 22:40:26

【2楼】 killin
注释很详细。谢谢!终于明白它的原理了。

eduhf_123 发表于 2009-11-30 01:39:38

我来捡个便宜,将程序写成可重定位的段的形式,修改入口参数与出口参数格式,制作成.LIB形式的库文件,可在C语言源文件中直接以“传递参数,取得返回值”的形式调用。
说明:
1、函数形式为“unsigned long bin2bcd(unsigned int)”,不再使用全局变量传递参数;
2、使用时将.H文件与.LIB文件复制到工程目录,包含.H文件并将.LIB文件添加进工程即可。
3、16位无符号整型参数通过R6、R7传递(C51采用大端格式,即R6中存高字节、R7中存低字节);
4、转成的压缩BCD码以“unsigned long int”32位无符号长整型的形式通过R4、R5、R6、R7返回(其中:R4中为随机
  值;R5高4位为0,低4位存BCD码的万位;R6高4位存千位,低4位存百位;R7高4位存十位,低4位存个位);
5、最多将花费57个机器周期,比原来的代码多了一个机器周期是因为入口参数与出口参数中的寄存器R6、R7重叠,原
  来代码中的倒数第二个XCH指令无法再使用,需要用两个MOV指令替代;
6、如果需要出口参数中的R4为0,可将源代码文件中的分号去掉,重新编译工程以得到合适的.LIB文件,代价为多使用
  2个字节的代码空间及2个额外的机器周期;
7、最后,附上Keil的完整工程及可用的.LIB库文件及.H头文件。

.LIB库文件、.H头文件、KEIL的整个工程ourdev_509015.rar(文件大小:4K) (原文件名:bin2bcd.rar)

eduhf_123 发表于 2009-11-30 01:46:22

其中的asm文件内容:
NAME    BIN2BCD

?PR?_bin2bcd?BIN2BCD    SEGMENT CODE INBLOCK
      PUBLIC_bin2bcd

      RSEG    ?PR?_bin2bcd?BIN2BCD
_bin2bcd:
      USING   0
      MOV   A,      R6
      ANL   A,      #0xFC
      RR      A
      RR      A
      MOV   R5,   A
      ADD   A,      R5
      ADD   A,      R5
      MOV   R4,   A
      MOV   A,      R7
      ANL   A,      #0x03
      MOV   R3,   A
      XRL   A,      R6
      ANL   A,      #0x03
      XRL   A,      R7
      RR      A
      RR      A
      ADD   A,      R4
      JNC   ?C0001
      INC   R5
      ADD   A,      #0x06
?C0001: ADD   A,      R4
      MOV   B,      #0x19
      JNC   ?C0002
      INC   R5
      ADD   A,      #0x06
      DIV   AB
      SJMP    ?C0003
?C0002: DIV   AB
      CJNE    A,      #0x0A,?C0003
      INC   R5
      CLR   A
?C0003: MOV   R6,   A
      MOV   A,      #0x0A
      XCH   A,      B
      ADD   A,      #(?C0004-$-3)
      MOVC    A,      @A+PC
      ADD   A,      R3
      DA      A
      MOV   R7,   A
      MOV   A,      R5
      DIV   AB
      MOV   R5,   A
      MOV   A,      B
      SWAP    A
      ORL   A,      R6
      MOV   R6,   A
      ;CLR    A
      ;MOV    R4,   A
      RET
?C0004: DB      0x00,   0x04,   0x08,   0x12,   0x16
      DB      0x20,   0x24,   0x28,   0x32,   0x36
      DB      0x40,   0x44,   0x48,   0x52,   0x56
      DB      0x60,   0x64,   0x68,   0x72,   0x76
      DB      0x80,   0x84,   0x88,   0x92,   0x96
      END

lyxing 发表于 2009-11-30 08:24:16

高手真不少,学习了。

jchqxl 发表于 2009-11-30 08:57:14

学习了。

shamork 发表于 2009-12-8 15:37:29

我又改了下。现在可以直接输入Int和一个idata数组的地址,出口就在这个idata的数组中。
这里说明下。keil中没有说明类型的指针都是通用指针,占3byte,而用unsigned char idata说明,就是指向unsigned char idata的指针了,只占1byte。



下面是完整的Keil工程
第一个是没有用lib的。第二个用了编译好的lib。进入调试,运行可以在串口1中看到输出。
点击此处下载 ourdev_511999.rar(文件大小:19K) (原文件名:bin2bcd2.rar)
点击此处下载 ourdev_512000.rar(文件大小:13K) (原文件名:bin2bcdlibtest.rar)

asm中 R4R5 分别是int的高低位,高位在前为R4. 压缩BCD在R1R2R3中。也是高位在前。
R4=0xff,R5=0xff出来 R1=0x06 R2=0x55 R3=0x35
buf=0x06 buf=0x055 buf=0x35

asm内容如下:

我用bin2bcd.c生成SRC然后改的。呵呵。比较笨。
; .\bin2bcd.SRC generated from: bin2bcd.c
; COMPILER INVOKED BY:
;      D:\Program Files\keil C 8.05\C51\BIN\C51.EXE bin2bcd.c ROM(SMALL) BROWSE DEBUG OBJECTEXTEND SRC(.\bin2bcd.SRC)

$NOMOD51

NAME        BIN2BCD
下面是reg51.h的内容,被我删掉了。你可以看上面的工程里的源文件,里面是全的。   
下面是bin2bcd的函数内容了
?PR?_bin2bcd?BIN2BCD SEGMENT CODE INBLOCK
        PUBLIC        _bin2bcd
; #include<reg51.h>
; #include<bin2bcd.h>
; void bin2bcd(unsigned char idata *bcd,unsigned int bin)

        RSEG?PR?_bin2bcd?BIN2BCD
_bin2bcd:
        USING        0

BIN2BCD:
        MOVA, R4         
        ANLA, #0FCH
        RR   A
        RR   A
        MOVR3,A
        ADDA, R3
        ADDA, R3
        MOVR1,A
        MOVA, R5
        ANLA, #3
        MOVR2,A
        XRLA, R4
        ANLA, #3
        XRLA, R5
        RR   A
        RR   A
        ADDA, R1
        JNCL2
        INCR3
        ADDA, #6
L2:      ADDA, R1
        MOVB, #25
        JNCL3
        INCR3
        ADDA, #6
        DIVAB
        SJMP L4
L3:      DIVAB
        CJNE A, #10, L4
        INCR3
        CLRA
L4:      MOVR1,A
        MOVA, #10
        XCHA, B
        ADDA, #(L5-$-3)
        MOVC A, @A+PC
        ADDA, R2
        DA   A
        XCHA, R3
        DIVAB
        XCHA, R1
        SWAP A
        ORLA, B
        SWAP A
        MOVR2,A
下面这几句是我加的了,其实可以跟上面的最后几句合并下,这样可以挤出几个周期来,但我改了一回,改不好。等高手来吧。      
        MOVR0,AR7          
        MOV@R0,AR1
        INCR0
        MOV@R0,AR2
        INCR0
        MOV@R0,AR3
        RET
L5:    DB 00H, 04H, 08H, 12H, 16H
       DB 20H, 24H, 28H, 32H, 36H
       DB 40H, 44H, 48H, 52H, 56H
       DB 60H, 64H, 68H, 72H, 76H
       DB 80H, 84H, 88H, 92H, 96H
       END

fshunj 发表于 2010-3-2 15:07:26

强悍,学习了

tonydh 发表于 2010-5-27 12:29:32

MARK

oufuqiang 发表于 2010-5-27 12:51:11

喜欢这类帖。

cowboy 发表于 2010-7-22 11:20:10

追加C51版本,82周期,个别数据需86或90周期,比汇编慢一些,但仍比传统算法快N++倍。
程序也很简洁,比汇编容易看。

#include <reg51.h>
unsigned char dig;

bin2bcd(unsigned int x)
{
    unsigned char i,j,k;
    k = x;
    i = x>>10;
    if (i > 41)        i++;
    j = (i+i+i)*2 + (x>>8<<6 | k>>2);
    if (CY == 1) {i++; j += 6;}
    if (j > 249) {i++; j += 6;}

    dig = i / 10;            //万位
    dig = B;               //千位
    dig = j / 25;            //百位
    dig = (B*4 | k%4) / 10;//十位
    dig = B;               //个位
}

eduhf_123 发表于 2010-7-22 11:52:28

回复【12楼】cowboy
追加c51版本,82周期,个别数据需86或90周期,比汇编慢一些,但仍比传统算法快n++倍。
程序也很简洁,比汇编容易看。
......
-----------------------------------------------------------------------

顶牛仔一下。

phone 发表于 2010-7-22 11:55:25

很不错。

xiesx1985 发表于 2010-11-28 15:14:42

都是牛人啊

hubeilcsun3 发表于 2011-1-6 22:46:24

mark

ERDTxiduoduo 发表于 2011-2-4 01:51:25

厉害,牛人。

wtiechen1969 发表于 2011-2-4 08:40:19

厉害,记号下

fangmcu 发表于 2011-2-4 08:47:20

牛人,谢谢!!

renpeng009 发表于 2011-2-4 09:44:05

mark

hemjidn 发表于 2011-2-4 10:40:22

晕了。51支持乘除法?为何不直接%10?
o……51应该不支持16位乘法吧呵又范迷糊了……

Your_Father 发表于 2011-2-4 10:58:59

mark..........

542433 发表于 2011-2-4 12:19:28

MARK

neverdead 发表于 2011-10-26 10:03:31

mark

jlian168 发表于 2013-7-14 11:37:45

cowboy 发表于 2010-7-22 11:20 static/image/common/back.gif
追加C51版本,82周期,个别数据需86或90周期,比汇编慢一些,但仍比传统算法快N++倍。
程序也很简洁,比汇 ...

SIR:

What is the C code for 32Bits?

Thank you.

kuanglf 发表于 2013-7-17 05:16:11

记号备用谢谢分享

leahcim89 发表于 2013-7-20 23:24:27

没看懂。。。

dtdzlujian 发表于 2013-7-21 14:58:24

挺好的 有时间研究一下
页: [1]
查看完整版本: 改造好方便keilC调用的最快16位2进制转压缩BCD 51库(关键字bin2BCD HEX int)