Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

32bit bin2BCD Casting

Status
Not open for further replies.

Suraj143

Active Member
Guys I need to covert 32 bit number to 8 digits bcd format.I did a little program.I need to cast this long vs short variables.Please help me.

I just looked at the converted assembly routine it looks like pretty inefficient :(

I use mikroC.

Code:
bin2bcd32(unsigned long int x){
//unsigned short bcd[8];
unsigned short digits = 8;

bin2bcd32_Loop:
            bcd[digits] = x % 10;
            if(x<=9)
                bcd[digits] = x;
            else{
                x = x / 10;
                  digits = digits - 1;
                   goto bin2bcd32_Loop;
            }
          }


Code:
_bin2bcd32:

;Freq Counter.c,149 :: 		bin2bcd32(unsigned long int x){
;Freq Counter.c,151 :: 		unsigned short digits = 6;
	MOVLW      6
	MOVWF      bin2bcd32_digits_L0+0
;Freq Counter.c,153 :: 		bin2bcd32_Loop:
___bin2bcd32_bin2bcd32_Loop:
;Freq Counter.c,154 :: 		bcd[digits] = x % 10;
	MOVF       bin2bcd32_digits_L0+0, 0
	ADDLW      _bcd+0
	MOVWF      FLOC__bin2bcd32+0
	MOVLW      10
	MOVWF      R4+0
	CLRF       R4+1
	CLRF       R4+2
	CLRF       R4+3
	MOVF       FARG_bin2bcd32_x+0, 0
	MOVWF      R0+0
	MOVF       FARG_bin2bcd32_x+1, 0
	MOVWF      R0+1
	MOVF       FARG_bin2bcd32_x+2, 0
	MOVWF      R0+2
	MOVF       FARG_bin2bcd32_x+3, 0
	MOVWF      R0+3
	CALL       _Div_32x32_U+0
	MOVF       R8+0, 0
	MOVWF      R0+0
	MOVF       R8+1, 0
	MOVWF      R0+1
	MOVF       R8+2, 0
	MOVWF      R0+2
	MOVF       R8+3, 0
	MOVWF      R0+3
	MOVF       FLOC__bin2bcd32+0, 0
	MOVWF      FSR
	MOVF       R0+0, 0
	MOVWF      INDF+0
;Freq Counter.c,155 :: 		if(x<=9)
	MOVF       FARG_bin2bcd32_x+3, 0
	SUBLW      0
	BTFSS      STATUS+0, 2
	GOTO       L__bin2bcd3241
	MOVF       FARG_bin2bcd32_x+2, 0
	SUBLW      0
	BTFSS      STATUS+0, 2
	GOTO       L__bin2bcd3241
	MOVF       FARG_bin2bcd32_x+1, 0
	SUBLW      0
	BTFSS      STATUS+0, 2
	GOTO       L__bin2bcd3241
	MOVF       FARG_bin2bcd32_x+0, 0
	SUBLW      9
L__bin2bcd3241:
	BTFSS      STATUS+0, 0
	GOTO       L_bin2bcd3220
;Freq Counter.c,156 :: 		bcd[digits] = x;
	MOVF       bin2bcd32_digits_L0+0, 0
	ADDLW      _bcd+0
	MOVWF      FSR
	MOVF       FARG_bin2bcd32_x+0, 0
	MOVWF      INDF+0
	GOTO       L_bin2bcd3221
L_bin2bcd3220:
;Freq Counter.c,158 :: 		x = x / 10;
	MOVLW      10
	MOVWF      R4+0
	CLRF       R4+1
	CLRF       R4+2
	CLRF       R4+3
	MOVF       FARG_bin2bcd32_x+0, 0
	MOVWF      R0+0
	MOVF       FARG_bin2bcd32_x+1, 0
	MOVWF      R0+1
	MOVF       FARG_bin2bcd32_x+2, 0
	MOVWF      R0+2
	MOVF       FARG_bin2bcd32_x+3, 0
	MOVWF      R0+3
	CALL       _Div_32x32_U+0
	MOVF       R0+0, 0
	MOVWF      FARG_bin2bcd32_x+0
	MOVF       R0+1, 0
	MOVWF      FARG_bin2bcd32_x+1
	MOVF       R0+2, 0
	MOVWF      FARG_bin2bcd32_x+2
	MOVF       R0+3, 0
	MOVWF      FARG_bin2bcd32_x+3
;Freq Counter.c,159 :: 		digits = digits - 1;
	DECF       bin2bcd32_digits_L0+0, 1
;Freq Counter.c,160 :: 		goto bin2bcd32_Loop;
	GOTO       ___bin2bcd32_bin2bcd32_Loop
;Freq Counter.c,161 :: 		}
L_bin2bcd3221:
;Freq Counter.c,162 :: 		}
	RETURN
; end of _bin2bcd32
 
Last edited:
All compiled C will be fairly inefficient. There is loads of moving between registers.

However, all binary to BCD conversion takes a lot of operations. There are several alternatives here:-

**broken link removed**
 
Sorry I have checked that page.The codes are wrong, it only works to the 1st digit..........even readers commented that codes are wrong.
 
There is a method called Double Dabble that doesn't require any multiply/division.

Here's a version I did a while ago,
Code:
void DoubleDabble(unsigned short long ShNum,char* String){
unsigned long dabble,Mask;
unsigned char i;
    dabble=0;
    for(i=0;i<24;i++){
        for(Mask=0xf0000000;Mask!=0;Mask>>=4){
            if((Mask&0x55555555)<=(Mask&dabble))
                dabble+=(Mask&0x33333333);
        }
        dabble<<=1;
        if(ShNum&0x800000)
            dabble++;
        ShNum<<=1;
    }
    for(i=0;i<8;i++){
        *String++=(dabble>>28)+0x30;
        dabble<<=4;
    }
    *String=0;
}
For an explanation of how it works see https://en.wikipedia.org/wiki/Double_dabble

Mike.
 
Sorry I have checked that page.The codes are wrong, it only works to the 1st digit..........even readers commented that codes are wrong.

I didn't check the code on Piclist. Code on there usually works.

A 32 bit number will need 10 BCD digits to be sure not to overflow. If you are converting to 8 BCD digits, it won't work if it over 0x05F5E0FF

You could try this code:-
http://www.piclist.org/techref/member/BB-LTL-/index.htm

It looks as though that has been checked.
 
There is a method called Double Dabble that doesn't require any multiply/division.

Here's a version I did a while ago,
Code:
void DoubleDabble(unsigned short long ShNum,char* String){
unsigned long dabble,Mask;
unsigned char i;
    dabble=0;
    for(i=0;i<24;i++){
        for(Mask=0xf0000000;Mask!=0;Mask>>=4){
            if((Mask&0x55555555)<=(Mask&dabble))
                dabble+=(Mask&0x33333333);
        }
        dabble<<=1;
        if(ShNum&0x800000)
            dabble++;
        ShNum<<=1;
    }
    for(i=0;i<8;i++){
        *String++=(dabble>>28)+0x30;
        dabble<<=4;
    }
    *String=0;
}
For an explanation of how it works see https://en.wikipedia.org/wiki/Double_dabble

Mike.

That's very nice mike.From the Wikipedia link I manage to understood your code :)
Anyway I like to write my own version bcuz I'm new to those pointer stuff :( I'll post my version here sooner.
 
Last edited:
@ Diver & Ian

This is ones of authors tested versions.But just apply D'128' (b'10000000') to accumulator & see.It never split to [1] [2] [8].Only 1st digit will make but not the others :(

Code:
Q_b2bcd
	clrf	BCD9         ;Clear all bcd digits
	clrf	BCD8
	clrf	BCD7
	clrf	BCD6
	clrf	BCD5
	clrf	BCD4
	clrf	BCD3
	clrf	BCD2
	clrf	BCD1
	clrf	BCD0
	movlw	D'32'        ;Outer loop
	movwf	bitcnt       ; bit counter
b2bcd1
	rlf	ACb0,f       ;Shift 32-bit accumulator
	rlf	ACb1,f       ; left to 
	rlf	ACb2,f       ;  put ms-bit 
	rlf	ACb3,f       ;   into Carry
	movlw	BCD0         ;Point to address of least 
	movwf	FSR          ; significant bcd digit
	movlw	D'10'        ;Inner loop
	movwf	digcnt       ; digit counter
b2bcd2
	rlf	INDF,f       ;Shift Carry into bcd digit
	movlw	D'10'        ;Subtract ten from digit then
	subwf	INDF,w       ; check and adjust for decimal overflow
	btfsc   STATUS,C     ;If Carry = 1 (result >= 0)
	movwf	INDF         ; adjust for decimal overflow
	decf	FSR,f        ;Point to next bcd digit
	decfsz	digcnt,f     ;Decrement digit counter
	goto	b2bcd2       ; - go if digcnt > 0
	decfsz	bitcnt,f     ;Decrement bit counter
	goto	b2bcd1       ; - go if bitcnt > 0
	retlw   0
 
Last edited:
I know it works but I'm not sure how efficient it is. However, I find it fascinating as it converts in a non obvious way. I love code that makes you think.

Mike.
 
there is a simple silly way to do it ;)

Code:
u16 hexTobcd(u16 v)
{
   int m,c,d;

   m = v/1000;
   v -= m*1000;
   c = v/100;
   v -= c*100;
   d = v/10;
   v -= d*10;  
   
   return(m<<12 | c<<8 | d<<4 | v);
}

the DoubleDabble is nice and fast, but sometime a easier one to understand is better :p

@Ian Rogers
"I was going to pack it into four bytes, as bin to ascii is already available via sprintf(); "

you should not use sprintf :) sprintf do a lot stuff for nothing .... take a peek at itoa() and atoi()

;)
 
Last edited:
there is a simple silly way to do it ;)

Code:
u16 hexTobcd(u16 v)
{
   int m,c,d;

   m = v/1000;
   v -= m*1000;
   c = v/100;
   v -= c*100;
   d = v/10;
   v -= d*10;  
   
   return(m<<12 | c<<8 | d<<4 | v);
}

the DoubleDabble is nice and fast, but sometime a easier one to understand is better :p

@Ian Rogers
"I was going to pack it into four bytes, as bin to ascii is already available via sprintf(); "

you should not use sprintf :) sprintf do a lot stuff for nothing .... take a peek at itoa() and atoi()

;)

Note that we are dealing with small micro controllers, not with stand alone PC programs.The above method will generate an extremely poor code that will waste lots of limited RAM bytes & program space plus executing time.

At first glance I also did similar code & I gave off after simulating its generated ASM code.
 
Last edited:
Note that we are dealing with small micro controllers, not with stand alone PC programs.The above method will generate an extremely poor code that will waste lots of limited RAM bytes & program space plus executing time.

At first glance I also did similar code & I gave off after simulating its generated ASM code.


"here is a simple silly way to do it"
yep but it took me 5 minutes to write and validate ... so i can continue focusing on my harder interrupt synchronization part :eek:P
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top