改造好方便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 mark 支持楼主,我把原程序也简单的注释一下
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 【2楼】 killin
注释很详细。谢谢!终于明白它的原理了。 我来捡个便宜,将程序写成可重定位的段的形式,修改入口参数与出口参数格式,制作成.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) 其中的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
高手真不少,学习了。 学习了。 我又改了下。现在可以直接输入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 强悍,学习了 MARK 喜欢这类帖。 追加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; //个位
} 回复【12楼】cowboy
追加c51版本,82周期,个别数据需86或90周期,比汇编慢一些,但仍比传统算法快n++倍。
程序也很简洁,比汇编容易看。
......
-----------------------------------------------------------------------
顶牛仔一下。 很不错。 都是牛人啊 mark 厉害,牛人。 厉害,记号下 牛人,谢谢!! mark 晕了。51支持乘除法?为何不直接%10?
o……51应该不支持16位乘法吧呵又范迷糊了…… mark.......... MARK mark 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. 记号备用谢谢分享 没看懂。。。 挺好的 有时间研究一下
页:
[1]