Searching \ for 'Binary to BCD' in subject line. ()
Help us get a faster server
FAQ page: www.piclist.com/techref/index.htm?key=binary+bcd
Search entire site for: 'Binary to BCD'.

Truncated match.
'Binary to BCD'
1997\03\03@211117 by

In comp.arch.embedded a discussion went on, for a while, about the merits of
assembler verses C.
Don Yuniskis posted this little gem as an example of an efficient 16 bit
Binary to
4 digit Binary Coded Decimal routine.

Don wrote:
-->>
Here's one version of a binary-decimal conversion routine.
Call with r.hl having binary value; returns 4 digit packed BCD
in r.de -- I neglected the fifth digit  :-(  It can be
rewritten to preserve that extra digit.  To produce ASCII,
obviously you can append a code fragment to add '0' to each
unpacked digit.

BinDec: LD B,16         ;7
LD DE,0         ;10

LD A,E          ;4
DAA             ;4
LD E,A          ;4

LD A,D          ;4
DAA             ;4
LD D,A          ;4

DJNZ Loop       ;13/8

RET             ;10
<<--

I wrote:
Of course it only works if the processor has a
DAA instruction.  (Decimal Adjust Accumulator).  I mentioned at the time
that both the PIC
and the 68HC05 don't have a DAA instruction and so his routine wouldn't
work.  To date I
have been using the slower 16 bit to 5 digit algo. that uses the divide and
makes use of
the location that holds the remainder after a divide.   It uses three bytes
of RAM and
calls the divide routine.  Not a big deal if 16 bit divide is used elsewhere
and has to
be part of the application anyway.

-->>
unsigned long
ubcd(void) {
unsigned long ulResult;  // Working result and arg.
unsigned int i;        // loop counter

ulResult = 0;

for (i=0; i<16; i+=4) {
ulArg = ulArg / 10;           // get rid of LS Digit
long_q <<= i;        // put remainder in correct position.
ulResult |= long_q;    // add to result
}

return ulResult;           // packed BCD.
};
<<--

However the penalty in using the divide is fairly high as it shifts through
the 16 bit
quotient 16 times.  Don's routine looked attractive as it also walks through
the 16 bit
binary value but only once, not five times.

So I have cloned the DAA instruction
and rewritten his routine to also generate 5 digits instead of 4.  It's
written in C and
before anyone has a heart failure be aware that I wrote it in Assembler
first.  Then
re-wrote it in C in a way that simulated as much of my assembler as
possible.  This makes
it easier to shove it around in the source file without worrying about page
bits etc.
Assembler would be a few bytes smaller where the C compiler generates
redundant code.

The goto is there to allow this routine to be replicated three times instead
of being
used as a subroutine.  Why?  Because the 16C57 only has a two level stack.

BTW,  Dag Bakken  should note that I do indeed take advantage of the type of
code
generated by the compiler when I:

"argument + 0x66;  // set C,DC flags with trial add."

I still believe that a compiler should not even generate code for this but
the MPC does
and I take advantage of it.  It could 'break' in the future though with a
new compiler release which is why I really don't like doing it. <grin>

//  Argument is the value to decimal adjust.
//  Must be called immediately after addition in order to not lose
//  status of CARRY and DIGIT CARRY bits in status register.
// ie:  a hidden argument is the status register.

unsigned char
DAA(unsigned char argument) {
Flags.DAA_FLAG = 0;      // Flag for doing code twice.
Flags.Carry = STATUS.C;  // Mustn't clear if already set.

while (1) {              // this could be a label that we goto.
DAA_VALUE = 0;   // Clear DAA adder.

if (STATUS.DC) {     // if a Half carry occured then make BCD
DAA_VALUE = 6;   // by adding 6 lower digit.
};
if (STATUS.C) {         // If a full carry occured make BCD digit by
DAA_VALUE |= 0x60;  // adding 6 to upper digit.
};

// If digits overflowed from addition
// we now make them BCD again.
argument += DAA_VALUE;  // DAA_VALUE can be 0x00, 0x06, 0x66

if (Flags.DAA_FLAG)  // We do above code twice.
goto DONE_DAA;   // This is how we get out.
Flags.DAA_FLAG = 1;

// Secondly test if BCD digits are in 'A'..'F' range byt a trial add.
argument + 0x66;    // set flags with trial add.
};
DONE_DAA:
// Carry might be already be 1 so we cannot just assign passed parameter
CARRY flag
// which might have been zero.
if (Flags.Carry) STATUS.C = 1;
return(argument);
};

unsigned char de0,de1,de2;
int counter;
unsigned long hl;

// Binary to Decimal Conversion routine  16bit to 5 digit.
// Parameter is passed in global var. 'hl' and is cleared when complete.
// result is in 'de0,de1,de2' where 'de0' is least sig digits.
// Uses 8 bit counter to walk through binary number.
void
BinDec() {
de0 = 0;       // Clear result accumulator.
de1 = 0;
de2 = 0;
counter = 16;  // All 16 bits of argument.
do {
hl <<= 1;  // Put MSb into Carry.

// Now if we had a ADC instruction this part wouldn't be needed.
WREG = 0;
if (STATUS.C)
WREG = 1;
// Simulate the Add with carry operation.
// At first glance it appears like a RLF would do the same thing but
// we need the STATUS.DC set if there is a digit overflow.
// 68HC05 does have ADC instruction.
de0 += de0 + WREG;
de0 = DAA(de0);

// if there was an overflow from the previous addition add it into
// the next BCD digits.
WREG = 0;
if (STATUS.C)
WREG = 1;
de1 += de1 + WREG;
de1 = DAA(de1);

WREG = 0;
if (STATUS.C)
WREG = 1;
de2 += de2 + WREG;
de2 = DAA(de2);

} while (--counter);
};

Finally I do welcome all input.  Especially if someone can improve on the
speed/space of this one.

Cheers,

John.
Pioneers are the ones, face down in the mud,
with arrows in their backs.
Automation Artisans Inc.      Ph. 1-250-544-4950
PO Box 20002                  Fax 1-250-544-4954
{Quote hidden}

Hmmm... suspiciously like Z80 code, even down to the clock cycle
counts, no?

> Finally I do welcome all input.  Especially if someone can improve on the
> speed/space of this one.

DAA is a bit tekno for this task.  My philosophy when it comes to
binary to decimal decoding is that decimal is almost always used
by humans (or printers) and hence speed is not an overriding concern.
I would go for the most compact code.  Successive subtraction of
powers of ten would be my first choice.  Actual division is
certainly unnecessary.  Converting an unsigned 16-bit number to
5-digit decimal requires a maximum of 27 2-byte subtractions,
20 1-byte subtractions and 5 additions.

Yes of course there are much smarter ways, but this is easy to
understand and can be implemented fairly easily (even on a PIC).

Now I realise there are always exceptions, but if you find yourself
requiring more than a few decimal conversions per second, then rethink
the application because no human can read that fast.

Regards,
SJH
Canberra, Australia
At 04:41 PM 04/03/1997 EST, you wrote:
{Quote hidden}

Yup!
{Quote hidden}

>
>Yes of course there are much smarter ways, but this is easy to
>understand and can be implemented fairly easily (even on a PIC).
>
>Now I realise there are always exceptions, but if you find yourself
>requiring more than a few decimal conversions per second, then rethink
>the application because no human can read that fast.

Well,  the 16C57 doesn't have interrupts.  I need to service I/O  quite
frequently.  I can't afford to spend a large amount of time in a subroutine
- including divide.  The above algo.  lends itself nicely to an iterative
approach.

ie: Using a flag, enter the routine, shift and calculate, leave the routine.
Do this 16 times and then set another flag that a different routine can use
as a conversion complete signal.  If tiem wasn't an object here or I was
using a PIC/8HC05 with interrupt support for time critical I'd stay with the
divide algo.  Short, simple, easy to follow as long as the 16 bit divide was
needed elsewhere.

Reagrds,

John

Pioneers are the ones, face down in the mud,
with arrows in their backs.
Automation Artisans Inc.      Ph. 1-250-544-4950
PO Box 20002                  Fax 1-250-544-4954
>
>BinDec: LD B,16         ;7
>        LD DE,0         ;10
>
>
>        LD A,E          ;4
>        DAA             ;4
>        LD E,A          ;4
>
>        LD A,D          ;4
>        DAA             ;4
>        LD D,A          ;4
>
>        DJNZ Loop       ;13/8
>
>        RET             ;10

Ah, Z-80, my first, true love.  And in Zilog mnemonics, no less!

Andy
==================================================================
Andy Kunz - Montana Design - 409 S 6th St - Phillipsburg, NJ 08865
Hardware & Software for Industry & R/C Hobbies
"Go fast, turn right, and keep the wet side down!"
==================================================================
> From: John Dammeyer <johndISLANDNET.COM>
> >[cut]
> >DAA is a bit tekno for this task.  My philosophy when it comes to
> >binary to decimal decoding is that decimal is almost always used
> >by humans (or printers) and hence speed is not an overriding concern.
> >I would go for the most compact code.  Successive subtraction of
> >powers of ten would be my first choice.  Actual division is
> >certainly unnecessary.  Converting an unsigned 16-bit number to
> >5-digit decimal requires a maximum of 27 2-byte subtractions,
> >20 1-byte subtractions and 5 additions.
>
> Please do post it.  ie: Put your opcodes down as evidence.

So you want me to put my code where my mouth is?  Well I've never
needed to actually do this (let alone on a '57, since I've always
used the '74 and '84).  Time does not permit me to give a real
implementation so you will have to be satisfied with pseudo C-code.
This code would translate in a fairly straightforward way into
16C57 code.  Obviously, some of the code could be 'commoned'.
Contrary to my previous post this will take a worst case, for
conversion of the number 5999x, of 26 2-byte subs, 10 1-byte subs,
3 2-byte adds and 2 1-byte adds -- not counting the digit increments.
The subtractions could be replaced by additions of -ve numbers if
that makes it easier.

The conversion procedure is broken up into very small bites so
that your non-interrupt-capable processor will have only a small
latency between checking for I/O etc.

enum state { start, finish, sub10k, sub1k, sub100, sub10 }
int binary
int divisor
char digit[5]

void bin2dec()
{
switch (state)
{
case start:
state = sub10k
digit[0] = digit[1] = ... = digit[4] = '0'
divisor = 10000
return
case sub10k:
binary -= divisor
if (binary < 0) [i.e. MSB of binary changed from 0 to 1]
{
binary += divisor
state = sub1k
divisor = 1000
}
else
digit[0]++
return
case sub1k:
binary -= divisor
if (binary < 0)
{
binary += divisor
state = sub100
divisor = 100
}
else
digit[1]++
return
case sub100:
binary -= divisor
if (binary < 0)
{
binary += divisor
state = sub10
divisor = 10
}
else
digit[2]++
return
case sub10:
binary -= divisor [S.B. - single byte op]
if (binary < 0)
{
binary += divisor [S.B.]
state = finish
digit[4] += binary [S.B.]
}
else
digit[3]++
return
}
}

main()
{
...
binary = [binary value to decode]
state = start
while (state != finish)
{
bin2dec()
[do something else e.g. service I/O]
}

print("decimal = %c%c%c%c%c", digit[0], digit[1], ..., digit[4])

}

Regards,
SJH
Canberra, Australia
At 11:12 AM 05/03/1997 EST, you wrote:
>> From: John Dammeyer <johndISLANDNET.COM>
<snip>

>The conversion procedure is broken up into very small bites so
>that your non-interrupt-capable processor will have only a small
>latency between checking for I/O etc.

I'll take a crack at it but I won't use the switch.  The extra code penalty
without the ability to use a jump table on a PIC is much too high.  This is
not meant as a criticism as I realize you whipped this out on the fly.  The
cost of walking down to 'case sub10:' nine times in a switch statement is
expensive.  I am not sure that this is actually any faster (or smaller) than
the DAA method. And yes,  negative constants are usually better.

I'll post the result when I'm done and like your example I'll make it small
bite capable.  BTW,  to be fair you need to pack the result;   mine was.
8-).  Or else I need to unpack mine 8-(.

Cheers,

John
Pioneers are the ones, face down in the mud,
with arrows in their backs.
Automation Artisans Inc.      Ph. 1-250-544-4950
PO Box 20002                  Fax 1-250-544-4954
John Dammeyer <PICLISTMITVMA.MIT.EDU> wrote:

> The extra code penalty without the ability to use a jump table on a
> PIC is much too high.

John:

Jump tables are trivially easy (and fast) on the PIC:

MOVF    STATE,W         ;switch (state)
;
GOTO    CASE0           ;    case 0: ....
GOTO    CASE1           ;    case 1: ....
GOTO    CASE2           ;    case 2: ....
etc...                  ;}

On some parts, you need to do a little fiddling with the PCLATH
register, but I'm sure you get the idea.

> I'll post the result when I'm done and like your example I'll make
> it small bite capable.  BTW,  to be fair you need to pack the
> result;   mine was. 8-).  Or else I need to unpack mine 8-(.

If you can deal with an unpacked result, just use the excellent
routine that John Payson posted here a few months ago.  It's
extremely fast, easily broken-up into small pieces, and it takes
very little code space.

Since John has inexplicably decided not to end this binary-to-BCD
thread by reposting it, I will (repost it, that is... Feel free

-------------------------------------------------------------------

;
; Binary-to-BCD.  Written by John Payson.
;
; Enter with 16-bit binary number in NumH:NumL.
; Exits with BCD equivalent in TenK:Thou:Hund:Tens:Ones.
;

org     \$0010   ;Start of user files for 16c84

NumH:   ds      1
NumL:   ds      1
TenK:   ds      1
Thou:   ds      1
Hund:   ds      1
Tens:   ds      1
Ones:   ds      1

Convert:                        ; Takes number in NumH:NumL
; Returns decimal in
; TenK:Thou:Hund:Tens:Ones
swapf   NumH,w
andlw   \$0F             ;*** PERSONALLY, I'D REPLACE THESE 2
addlw   \$F0             ;*** LINES WITH "IORLW 11110000B" -AW
movwf   Thou
movwf   Hund
movwf   Ones

movf    NumH,w
andlw   \$0F
movwf   Tens

swapf   NumL,w
andlw   \$0F

rlf     Tens,f
rlf     Ones,f
comf    Ones,f
rlf     Ones,f

movf    NumL,w
andlw   \$0F
rlf     Thou,f

movlw   \$07
movwf   TenK

; At this point, the original number is
; equal to
TenK*10000+Thou*1000+Hund*100+Tens*10+Ones ;
if those entities are regarded as two's
compliment ; binary.  To be precise, all of
them are negative ; except TenK.  Now the
number needs to be normal- ; ized, but this
can all be done with simple byte ; arithmetic.

movlw   \$0A                             ; Ten
Lb1:
decf    Tens,f
btfss   3,0
goto   Lb1
Lb2:
decf    Hund,f
btfss   3,0
goto   Lb2
Lb3:
decf    Thou,f
btfss   3,0
goto   Lb3
Lb4:
decf    TenK,f
btfss   3,0
goto   Lb4

retlw   0
-------------------------------------------------------------------

-Andy

=== Andrew Warren - fastfwdix.netcom.com
=== Fast Forward Engineering - Vista, California
===
=== www.geocities.com/SiliconValley/2499/fund.html
At 02:27 AM 05/03/1997 -0800, you wrote:
>John Dammeyer <PICLISTMITVMA.MIT.EDU> wrote:
>
>> The extra code penalty without the ability to use a jump table on a
>> PIC is much too high.
>
>    John:
>
>    Jump tables are trivially easy (and fast) on the PIC:
>
>        MOVF    STATE,W         ;switch (state)
>                                ;
>        GOTO    CASE0           ;    case 0: ....
>        GOTO    CASE1           ;    case 1: ....
>        GOTO    CASE2           ;    case 2: ....
>        etc...                  ;}
>
>    On some parts, you need to do a little fiddling with the PCLATH
>    register, but I'm sure you get the idea.

True.  In assembler this is a snap as long as it all fits in the one page.
If it doesn't then
there can be a second level of indirect jumps.  In either case a combination
of assembler and C would make Case Statements more efficient.

eg:
Jump tables are trivially easy (and fast) on the PIC when
jump tables are placed in the same page.

MOVF    STATE,W         ;switch (state)
;
GOTO    _CASE0           ;    case 0: ....
GOTO    _CASE1           ;    case 1: ....
GOTO    _CASE2           ;    case 2: ....

_CASE0:
BSF    STATUS,PA0
BCF    STATUS,PA1
GOTO   CASE0
_CASE1:
etc...

There is just more administration needed to keep all this organized.  It
would be nice it the C compiler gave me the option to have this type of case
statement.
{Quote hidden}

As promised.  Here is the C version of Steve Hardy's binary to BCD routine.
I've modified it to handle 16 bit unsigned integers.  It can be called from
within the mainline polling loop so that the processor isn't tied down to
doing conversions.

What is interesting is the speed and code size is much smaller than the DAA
simulation; kudos to Steve for the idea.  I've also taken advantage of
having access to the Carry flag to let it handle 16 bit unsigned 16 bit
ints.  With the exception of a few places where the compiler adds redundent
PA0,PA1 bit setting for GOTO support the code generated isn't much slower or
bigger than what would be generated by directly doing this in assembler.
The most interesting part is the time taken to do each little bit - about 43
clocks on average.  At 20MHz this is only 8.2 uSec and may be the best
technique to avoid impacting the polling of other I/O.

Size of DAA simulation method       94 instructions
Execution speed for 59,999 (0xEA5F)  -> 2798 clocks

Size of DECIMAL SUBTRACTION method  86 instructions
Execution speed                      -> 1575 clocks
Average execution time for each call ->   43 clocks

Size of BCD using 16 bit division   57 instructions
Execution speed                      -> 2543 clocks.

I have not included the 16/16 divide as I assume it would be used elsewhere
in the application.

Conclusions so far...

If you need 16 bit divide anyway and speed isn't a problem use the inelegant
divide technique; it's easy to understand and document and IMHO a well
documented and easy to understand program is part of a correctly functioning
program.

If you need speed and space ... use Steve Hardy's successive subtraction
method.  It could be made faster by doing doing all the conversion in one
time rather than in bits.

As for John Payson's method.  Haven't gotton to it yet.  But the assembler
version obviously takes 50 instructions.  How much time is spent in the
loops?  Don't know yet.  It will probably be the fastest and the smallest;
understanding how it works may be more difficult.

Now I must stop having fun get back to real work.  8-)

Cheers

John.

-->>
// Default for compiler is set to 8 bit ints and 16 bit longs.
bits bcdFlags;
#define COMPLETE bcdFlags.0

int digit[5];
unsigned long binary;
unsigned long divisor;
int digitNumber;

unsigned int
bin2dec () {
if (COMPLETE) {
digit[0] = 0;  // LSDigit
digit[1] = 0;
digit[2] = 0;
digit[3] = 0;
digit[4] = 0;  // MSdigit
COMPLETE = 0;
digitNumber = 4;
divisor = 10000;
}
else {
binary -= divisor;
if (!STATUS.C) {
binary +=divisor;
if (--digitNumber == 0) {
COMPLETE = 1;
digit[digitNumber] = binary;
}
else {
if (digitNumber == 3)
divisor = 1000;
else if (digitNumber == 2)
divisor = 100;
else
divisor = 10;
}
}
else {
digit[digitNumber]++;
};
};
return COMPLETE;
}

main() {
COMPLETE = 1;   // Signal that bin2dec() may convert a new value.
binary = 0;     // force to known value.
while (1) {

if (bin2dec()) {          // returns TRUE if conversion complete.
Display_BCD_Data();   // Put on LCD or LED display.
// Now get new data to display.
binary = CreateNewBinaryValueToDisplay();
};

// Do other real time processing every 10 uSeconds or so with a 20MHz
// processor.
}
}
<<--
Pioneers are the ones, face down in the mud,
with arrows in their backs.
Automation Artisans Inc.      Ph. 1-250-544-4950
PO Box 20002                  Fax 1-250-544-4954
John Dammeyer wrote:
>

>
> As for John Payson's method.  Haven't gotton to it yet.  But the assembler
> version obviously takes 50 instructions.  How much time is spent in the
> loops?  Don't know yet.  It will probably be the fastest and the smallest;
> understanding how it works may be more difficult.

Well, according to Bob Fehrenbach timing analysis:
(NB: 49 program memory locations.  Executes in
153 to 198 clock cycles (worst case I could find)  BF)

And according to my dissection (which I posted last November) in
response to a challenge from John:

If you have a 4 digit hexadecimal number, it may be written as

N = a_3*16^3 + a_2*16^2 + a_1*16 + a_0

where a_i, i=0,1,2,3 are the four hexadecimal digits. If you
wish to convert this to decimal, then you need to express N as

N = b_4*10^4 + b_3*10^3 + b_2*10^2 + b_1*10 + b_0

Where b_j, j=0,1,2,3,4 are the five decimal digits.
The reason there are 5 digits in the decimal representation
is because the maximum four digit hexadecimal number (0xffff)
requires 5 decimal digits (65535). Now the goal is to find
a set of equations that allow the b_j's to be expressed in
terms of the a_i's. There are infinitely many ways to do this.
Here are two of probably the simplest expressions.
First, expand the 16^i's and then collect all coefficients
of the 10^j's:

N = a_3*4096 + a_2*256 + a_1*16 + a_0
= a_3*4*10^3  + a_2*2*10^2 + (a_3*9 + a_2*5 + a_1)*10 +
6*(a_3 + a_2 + a_1) + a_0

This gives us five equations:

b_0 = 6*(a_3 + a_2 + a_1) + a_0
b_1 = a_3*9 + a_2*5 + a_1
b_2 = 2*a_2
b_3 = 4*a_3
b_4 = 0

Which as John says, must be "normalized". Normalization in
this context means we need to reduce the b_j's such that
0 <= b_j <= 9

In other words we need to find:

c_0 = b_0 mod 10

b_1 = (b_1 + (b_0 - c_0)/10)
c_1 =  b_1 mod 10

b_2 = (b_2 + (b_1 - c_1)/10)
c_2 = b_2 mod 10

b_3 = (b_3 + (b_2 - c_2)/10)
c_3 = b_3 mod 10

c_4 = (b_4 + (b_3 - c_3)/10) mod 10
= (b_2 - c_2)/10

Division by 10 can be done quite efficiently (as was shown
in another thread several months ago). However, it does require
a significant amount of code space compared to say repeated
subtractions. Unfortunately, there can be very many subtractions
that are required. For example, b_1 could be as large as 15*16,
or 240. 10 would have to be subtracted 24 times if you wish to
compute 240 mod 10. I presume John realized this inefficiency
and thus sought to express N so that repeated subtractions
could be used and that the total number of subtractions are
minimized. This leads to the next way that N can be expressed:

N = a_3*(4100 - 4) + a_2*(260 - 4) + a_1*(20-4) + a_0
= 4*a_3*10^3 + (a_3 + 2*a_2)*10^2 + (6*a_2 + 2*a_1)*10 +
a_0 - 4*(a_3 + a_2 + a_1)

This gives five new equations for the b_j's.

b_0 = a_0 - 4*(a_3 + a_2 + a_1)
b_1 = 6*a_2 + 2*a_1
b_2 = a_3 + 2*a_2
b_3 = 4*a_3
b_4 = 0

However, these equations are still not conducive to the
repeated subtraction algorithm, at least the way John has
done it. In other words, it is possible to pre-condition
each of the b_j's so that they are less than zero. Then
the repeated subtractions can simultaneously perform the
"mod 10" and "/10" operations shown above. Consider the
equation b_0 for example,

b_0 = a_0 - 4*(a_3 + a_2 + a_1)

Since each a_i must satisfy:  0 <= a_i <= 15, then b_0
ranges:

-60 <= b_0 <= 15

We can make b_0 negative by subtracting any number greater than
15. A logical choice is 20. This is because if we subtract 20
from b_0, we can add 2 to b_1 to keep the net result the same.
The reason we add "2" can be seen:

b_1*10 + b_0 = b_1*10 + b_0 + 20 - 20
= (b_1 + 2)*10 + b_0 - 20

Carrying this concept out for the rest of the b_i's we have.

b_0 = a_0 - 4*(a_3 + a_2 + a_1) - 20
b_1 = 6*a_2 + 2*a_1 + 2 - 140
= 6*a_2 + 2*a_1 - 138
b_2 = a_3 + 2*a_2 + 14 - 60
= a_3 + 2*a_2 - 46
b_3 = 4*a_3 + 6 - 70
= 4*a_3 - 64
b_4 = 0 + 7
= 7

And if you look at John's code closely, you will see this is
how he has expressed the b_j's.

Scott

PS The jury is out...
There is a borderline elegant routine for Binary to BCD conversion in
Appendix H of AN-526, which can be found in the Microchip Embedded
Control Handbook, 1995 editon.  The original version of this appeared to
the best of my knowledge in the General Instrument PIC Applications
Manual, August 1980 (Note: no typo... 1 9 8 0)

The routine as presented by GI/Microchip can be further optimized by
about 5 instructions and 100 or so cycles.  It can also be incorporated
into a divide routine (if you happen to need to convert the output of a
binary divide to BCD) for even greater instruction/execution economy.

Site Y

'Binary to BCD'
1998\02\28@191401 by
Anyone have any subs that convert a 12 bit binary serial input to 4 BCD
numbers that are possibly stored in registers to send out to to
displays?

thanks in advance for any suggestions

'Binary to BCD'
1998\03\02@051214 by
Bill, Try this code. I have used it in a recent project. I hope it helps

Peter.

;
***************************************************************************
; ASSIGN LABELS TO THE VARIOUS (RAM) DATA FILE REGISTERS USED
;
***************************************************************************
R0              RES     1               ; DECIMAL STORE FOR 1S DIGIT
R1              RES     1               ; DECIMAL STORE FOR 10S DIGIT
R2              RES     1               ; DECIMAL STORE FOR 100S DIGIT

DIGIT1          RES     1               ; STORE FOR RIGHT DISPLAY DATA
DIGIT2          RES     1               ; STORE FOR R/MIDDLE DISPLAY DATA
DIGIT3          RES     1               ; STORE FOR L/MIDDLE DISPLAY DATA
DIGIT4          RES     1               ; STORE FOR LEFT DISPLAY DATA

; *******************************************************
; * BCD CONVERSION
; *******************************************************
B2_BCD  MOVFW   COUNTER_LOW
MOVWF   IPBUFFL
MOVFW   COUNTER_HIGH
MOVWF   IPBUFFH
BCF     STATUS,CARRY            ; NOW CONVERT THE 16 BIT NUMBER
MOVLW   16                      ; INTO 5 BCD CHARACTERS - THE UPPER BCD
MOVWF   COUNT1                  ; IS NOT DISPLAYED BUT HAS BEEN LEFT IN
CLRF    R0                      ; FOR FUTURE USE
CLRF    R1
CLRF    R2

LOOP16  RLF     IPBUFFL,F               ; COUNT BUFFER LOW BYTE
RLF     IPBUFFH,F               ; COUNT BUFFER HIGH BYTE
RLF     R2,F
RLF     R1,F
RLF     R0,F
DECFSZ  COUNT1,F                ;DECRIMENT AND SKIP IF ZERO
GOTO    BCDCON

MOVWF   FSR
MOVLW   R1
MOVWF   FSR
MOVLW   R0
MOVWF   FSR
GOTO    LOOP16
BCDCON                          ; CONVERT THE 4 BCD DIGITS INTO THE CORRECT
SWAPF   R1,W            ; FORMAT FOR THE LED BIT PATTEN.
ANDLW   0FH                     ; STRIP UPPER BITS
CALL    GETCHAR
MOVWF   DIGIT4
MOVFW   R1
ANDLW   0FH                     ; STRIP UPPER BITS
CALL    GETCHAR
MOVWF   DIGIT3
SWAPF   R2,W
ANDLW   0FH                     ; STRIP UPPER BITS
CALL    GETCHAR
MOVWF   DIGIT2
MOVFW   R2
ANDLW   0FH                     ; STRIP UPPER BITS
CALL    GETCHAR
MOVWF   DIGIT1
RETURN

;***************************************************************************
**
;***************************************************************************
**
MOVWF   TEMP
BTFSC   TEMP,3                  ; TEST IF RESULT > 7
MOVWF   0
MOVLW   30H
MOVWF   TEMP
BTFSC   TEMP,7                  ; TEST IF RESULT > 7
MOVWF   0                       ; SAVE AS MSD
RETLW   0

;***************************************************************************
**
; GET A DISPLAY CHARACTER BIT PATTERN BY CONVERTING 'W' TO DISPLAY CODE
;***************************************************************************
**
GETCHAR
ADDWF   PC,F            ; USE 'W' AS AN OFFSET FOR 'PC'
RETLW   B'00111111'     ; RETURN WITH "0" DISPLAY CODE
RETLW   B'00000110'     ; RETURN WITH "1" DISPLAY CODE
RETLW   B'01011011'     ; RETURN WITH "2" DISPLAY CODE
RETLW   B'01001111'     ; RETURN WITH "3" DISPLAY CODE
RETLW   B'01100110'     ; RETURN WITH "4" DISPLAY CODE
RETLW   B'01101101'     ; RETURN WITH "5" DISPLAY CODE
RETLW   B'01111101'     ; RETURN WITH "6" DISPLAY CODE
RETLW   B'00000111'     ; RETURN WITH "7" DISPLAY CODE
RETLW   B'01111111'     ; RETURN WITH "8" DISPLAY CODE
RETLW   B'01100111'     ; RETURN WITH "9" DISPLAY CODE
RETLW   B'01110111'     ; RETURN WITH "A" DISPLAY CODE
RETLW   B'01111100'     ; RETURN WITH "B" DISPLAY CODE
RETLW   B'00111001'     ; RETURN WITH "C" DISPLAY CODE
RETLW   B'01011110'     ; RETURN WITH "D" DISPLAY CODE
RETLW   B'01111001'     ; RETURN WITH "E" DISPLAY CODE
RETLW   B'01110001'     ; RETURN WITH "F" DISPLAY CODE

====================================
Remember .... Every Silver Lining
Has It's Cloud
====================================
Mailto:efoccyberstop.net

'Binary to BCD'
1998\10\14@205851 by
Hi all,

I am trying to do 16 bit binary to BCD conversion and it is becomming much
more complicated than I had initially anticipated. As far as I can see,
there are only two ways to accomplish it:

1) Integer Division by 10 (and using the remainders as the resultant digits)

2) Using a lookup table for each resultant digit to find the "contribution"
from each bit of the input number (i.e., for the hundreds digit, bits 0
thru 6 contribute zero, but bit 7 adds 100, bit 8 adds 200, bit 9 adds 500,
so you add up each of these contributions, modulo ten, and place the result
as the hundreds digit, and carry over what's left to the next digit's
contribution).

Both of these take up considerable code and register space (for method one,
I'd need a 16bit division by ten routine as well as several pairs of 8bit
file regs, for method 2 I'd need about 40 words of lookup space in addition
to some code).

Can someone provide a sanity check? Is there something I'm not seeing?

I realize that there is probably boilerplate code out there for this, and I
may ultimately resort to that, but for now, I only want to borrow ideas
from it at most, I want to write this code for its educational value.

Thanks,

Sean

+-------------------------------+
| Sean Breheny                  |
| Electrical Engineering Student|
+-------------------------------+
Save lives, please look at http://www.all.org
Personal page: http://www.people.cornell.edu/pages/shb7
shb7cornell.edu  Phone(USA): (607) 253-0315 ICQ #: 3329174

> I am trying to do 16 bit binary to BCD conversion and it is becomming
> much more complicated than I had initially anticipated. As far as I
> can see, there are only two ways to accomplish it:

> 1) Integer Division by 10 (remainders [are] digits)

> 2) Using a lookup table for each resultant digit to find the
> "contribution" from each bit of the input number

3) repeated subtraction of 10000, 1000, 100, 10; remainder is
1s digit.  You do need a 16-bit subtract routine.

Lee Jones

Hi Lee,

Thanks for the idea. I'll give it a try and see how it compares in size and
resources. It certainly looks better.

Sean

At 06:08 PM 10/14/98 -0700, you wrote:
>> I am trying to do 16 bit binary to BCD conversion and it is becomming
>> much more complicated than I had initially anticipated. As far as I
>> can see, there are only two ways to accomplish it:

[SNIP]

>
>3) repeated subtraction of 10000, 1000, 100, 10; remainder is
>   1s digit.  You do need a 16-bit subtract routine.
>
>                                                Lee Jones
>
+-------------------------------+
| Sean Breheny                  |
| Electrical Engineering Student|
+-------------------------------+
Save lives, please look at http://www.all.org
Personal page: http://www.people.cornell.edu/pages/shb7
shb7cornell.edu  Phone(USA): (607) 253-0315 ICQ #: 3329174

Lee and everyone else,

Yup. That is much easier. I don't know why, but when I was trying to come
up with a way to do this, that method seemed to have way too many
subtractions in it, but it actually has a maximum of 7+6+6+4+6=29 total 16
bit subtractions for the entire 16 bit conversion.

Thanks again,

Sean

At 06:08 PM 10/14/98 -0700, you wrote:
{Quote hidden}

+-------------------------------+
| Sean Breheny                  |
| Electrical Engineering Student|
+-------------------------------+
Save lives, please look at http://www.all.org
Personal page: http://www.people.cornell.edu/pages/shb7
shb7cornell.edu  Phone(USA): (607) 253-0315 ICQ #: 3329174

>I am trying to do 16 bit binary to BCD conversion and it is becomming much
>more complicated than I had initially anticipated. As far as I can see,
>there are only two ways to accomplish it:
>
>1) Integer Division by 10 (and using the remainders as the resultant digits)
>
>2) Using a lookup table for each resultant digit to find the "contribution"
>from each bit of the input number (i.e., for the hundreds digit, bits 0
>thru 6 contribute zero, but bit 7 adds 100, bit 8 adds 200, bit 9 adds 500,
>so you add up each of these contributions, modulo ten, and place the result
>as the hundreds digit, and carry over what's left to the next digit's
>contribution).

Try door number 3:

save number into reg
clrf 10ks
clrf 1ks
clrf 100s
clrf 10s
clrf 1s

loop1
subtract .10000,W (use appropriate routine: make sure it preserves C)
skpc                ;note result is in W
goto  sub_1k      ;underflow?
movwf   reg         ;nope, so save new value
incf    10ks        ;increment 10 thousands register
goto loop1          ;do it again

sub_1k
subtract .1000

I'm sure you can see where this is going . . .

It takes a while (6 loops max for 10ks, 10 loops max for the each of the
other loops) but doesn't take too much space.  Otherwise, look for Johm
Payson's neat routine that does it faster (much faster!) AND in less space!

Hope this helps.

dwayne

Dwayne Reid   <dwaynerplanet.eon.net>
Trinity Electronics Systems Ltd    Edmonton, AB, CANADA
(403) 489-3199 voice     (403) 487-6397 fax

[SNIP]

>Lee and everyone else,
>
>Yup. That is much easier. I don't know why, but when I was trying to come
up with a way to do >this, that method seemed to have way too many
subtractions in it, but it actually has a >maximum >of 7+6+6+4+6=29 total
16 bit subtractions for the entire 16 bit conversion.

OOPS. That should have said 6+10+10+10+10=46.

Sean

+-------------------------------+
| Sean Breheny                  |
| Electrical Engineering Student|
+-------------------------------+
Save lives, please look at http://www.all.org
Personal page: http://www.people.cornell.edu/pages/shb7
shb7cornell.edu  Phone(USA): (607) 253-0315 ICQ #: 3329174

Hi Duane and everyone else,

This binary to BCD conversion is really beginning to show me how little I

At 09:55 PM 10/14/98 -0400, you wrote:
>loop1
>    subtract .10000,W (use appropriate routine: make sure it preserves C)
>    skpc                ;note result is in W
>      goto  sub_1k      ;underflow?
>    movwf   reg         ;nope, so save new value
>    incf    10ks        ;increment 10 thousands register
>    goto loop1          ;do it again

Well, I understand what you are saying, and if I write a BASIC program in
QBASIC to do this, it works fine (using the built-in subtract routine).

However, now I am trying to port this to my PIC and I am having problems
writing a subtract routine:

Here's what I tried:

>>>>CODE STARTS HERE

; SUB16
; Does AB-CD -> AB
; Destroys A,B,W,Carry,DigitCarry
; Returns A,B,Carry

rA      equ     0x0C
rB      equ     0x0D
rC      equ     0x0E
rD      equ     0x0F

main    call sub16
main2   goto main2

sub16   movf rD,W       ;Do B-D -> B
subwf rB,F
movlw 1 ;Do the borrow from A if needed
skpc
subwf rA,F
movf rC,W       ;Do A-C -> A
subwf rA,F
return          ;Done
END

>>>>CODE ENDS HERE

When I simulate this, the result seems to be correct. However, the carry
out does not follow the rules required to be used in the above routine
provided by Duane. The reason for this is that the last subtraction (subwf
rA,F), considers rA to be a positive number, regardless of its MSB (this
doesn't affect the answer, but it does affect the carry). I could make the
routine more complicated and make a carry out which follows the criteria
needed for the routine, but why not just use the MSB of the output to tell
if it is positive or negative?
Also, what SHOULD the carry out be for such a subtract routine? If you
simply execute SUBWF A,F and A contains a negative number and W contains a
small positive number, the PIC will call the output positive ( nBORROW = 1
). Should my routine do the same if fed a negative number in AB? I realize
that it doesn't really matter for the BCD conversion application, but I
just want to understand how to write a general 16 bit subtract routine.

Thanks,

Sean

+-------------------------------+
| Sean Breheny                  |
| Electrical Engineering Student|
+-------------------------------+
Save lives, please look at http://www.all.org
Personal page: http://www.people.cornell.edu/pages/shb7
shb7cornell.edu  Phone(USA): (607) 253-0315 ICQ #: 3329174

OOPS AGAIN! I must be very tired. Sorry, Dwayne. I TOTALLY mispelled your
name in that last message.

Sean

+-------------------------------+
| Sean Breheny                  |
| Electrical Engineering Student|
+-------------------------------+
Save lives, please look at http://www.all.org
Personal page: http://www.people.cornell.edu/pages/shb7
shb7cornell.edu  Phone(USA): (607) 253-0315 ICQ #: 3329174

I think I found my mistake.

This is what the subtract routine should look like:

sub16   movf rD,W       ;do B-D -> B
subwf rB,F
movf rC,W       ;load C into W
skpc            ;add one to W to do borrow if needed
subwf rA,F      ;Do A-C -> A
return

This preserves the carry by combining the borrow and the final subtraction
into one subwf

Thanks for all your help and I'll probably be back with more questions,

Sean

+-------------------------------+
| Sean Breheny                  |
| Electrical Engineering Student|
+-------------------------------+
Save lives, please look at http://www.all.org
Personal page: http://www.people.cornell.edu/pages/shb7
shb7cornell.edu  Phone(USA): (607) 253-0315 ICQ #: 3329174

Sean Breheny wrote:
> Also, what SHOULD the carry out be for such a subtract routine?

A - B

if B = A then C = 1, Z = 1
if B < A then C = 1, Z = 0
if B > A then C = 0, Z = 0

--
Best regards

Tony

Multimedia 16F84 Beginners PIC Tools.
**New Improved PicNPost**

http://www.picnpoke.com
Email picnpokecdi.com.au

Hi Tony,

At 03:42 PM 10/15/98 +1000, you wrote:
>Sean Breheny wrote:
>> Also, what SHOULD the carry out be for such a subtract routine?
>
>A - B
>
>if B = A then C = 1, Z = 1
>if B < A then C = 1, Z = 0
>if B > A then C = 0, Z = 0
>

Yes, of course, I understand that, but my point is "how do we tell if B>A
or B<A"? I ask this because we are considering A to be an unsigned number
and B to be signed. We can consider them both to be signed, and the result
doesnt change, but should the carry then be different, since for A=-1 B =
2, B is really greater than A, but when we feed the two's complement
numbers into our routine, we would get "A=0xFF B=0x02" and it would
conclude that A>B.

Sean

+-------------------------------+
| Sean Breheny                  |
| Electrical Engineering Student|
+-------------------------------+
Save lives, please look at http://www.all.org
Personal page: http://www.people.cornell.edu/pages/shb7
shb7cornell.edu  Phone(USA): (607) 253-0315 ICQ #: 3329174

that method seemed to have way too many subtractions in it, but
it actually has a maximum of 7+6+6+4+6=29 total 16 bit
subtractions for the entire 16 bit conversion.

Ok, how did you get that sum?  I get something like 35
subtractions (which still isn't that bad) for 59999...  I guess
the last 10 subtracts are only 8-bit, and an additional 10
subtracts are an 8 bit quantity from a 16 bit value.

BillW

Hi Bill,

At 11:00 PM 10/14/98 PDT, you wrote:
>    that method seemed to have way too many subtractions in it, but
>    it actually has a maximum of 7+6+6+4+6=29 total 16 bit
>    subtractions for the entire 16 bit conversion.
>
>Ok, how did you get that sum?  I get something like 35
>subtractions (which still isn't that bad) for 59999...  I guess
>the last 10 subtracts are only 8-bit, and an additional 10
>subtracts are an 8 bit quantity from a 16 bit value.

Yeah, you're right. I goofed that one totally. I was thinking that 65535
would yield the maximum number of subtractions. There must be a time delay
between me-the list-and you because I already sent out a message attempting
to correct what I had said before. I got 46 in that message, but that was
counting every subtraction as a 16-bit subtraction, and it was counting
subtractions for the ones  place as well!! So, it should be (if I can trust
my brain now!) 6+10+10=26 16-bit subtractions and then 10 more 8-bit ones
(for the tens place and the ones remainder).

WOW! I really have to check my math before I post to 1600 people!

Thanks for the correction,

Sean

>
>BillW
>
+-------------------------------+
| Sean Breheny                  |
| Electrical Engineering Student|
+-------------------------------+
Save lives, please look at http://www.all.org
Personal page: http://www.people.cornell.edu/pages/shb7
shb7cornell.edu  Phone(USA): (607) 253-0315 ICQ #: 3329174

Sean Breheny wrote:
> I ask this because we are considering A to be an unsigned number
> and B to be signed.

It would obviously get tricky if one number was signed and the other
not. The signed value can never be more than half the value of the
unsigned value, assuming the same bit length. Therefore, if the MSB of
the unsigned number = 1, then it will be the larger value. If the MSB of
the signed value is negative, then it will always be the lower value. If
both numbers are positive, with number A MSB = 0, then you would need to
test for the larger of the 2 positive values.

--
Best regards

Tony

Multimedia 16F84 Beginners PIC Tools.
**New Improved PicNPost**

http://www.picnpoke.com
Email picnpokecdi.com.au

Hi,

> Lee and everyone else,
>
> Yup. That is much easier. I don't know why, but when I was trying to come
> up with a way to do this, that method seemed to have way too many
> subtractions in it, but it actually has a maximum of 7+6+6+4+6=29 total 16
> bit subtractions for the entire 16 bit conversion.
>
> Thanks again,
>
> Sean
>

[snip]

> >3) repeated subtraction of 10000, 1000, 100, 10; remainder is
> >   1s digit.  You do need a 16-bit subtract routine.
> >

I usually use the plain old divide by 10 and get the remainder method.
The routine is small (16 words, including the Z flag stuff at the end)
and it executes reasonably fast (about 185 cycles).  I doubt if 30+
16 bit subtractions would be significantly faster or smaller (if any).
The divide routine also scales extremely easy - A small increase in
size for every byte and an almost linear increase in execution time.  The
only drawback of the divide scheme is that you receive your digits from
right to left and not left to right.

;=========================================================================
;Divide the 16 bit number in TempNum by 10 and store the result
;in TempNum and the remainder in Temp.  If the quotient and the
;remainder is 0, the Z flag is set.  This is usefull for leading 0
;suppression.
;=========================================================================
Div10:
movlw   16
movwf   C1                      ;Repeat for 16 bits
clrf    Temp
Div10Loop:
rlf     TempNum, W
rlf     TempNum+1, F
rlf     Temp, F                 ;Move MSB of Number into Temp
movlw   10
subwf   Temp, W                 ;Does 10 go in?
btfsc   STATUS, C
movwf   Temp                   ;If so, update remainder
rlf     TempNum, F              ;If 10 went in, shift in a 1, if
;not shift in a 0
decfsz  C1, F
goto   Div10Loop               ;Repeat for all bits

movf    TempNum, W              ;Test if Quotient is 0
iorwf   TempNum+1, W            ;Test if Quotient is 0
iorwf   Temp, W                 ;Test if Remainder is also 0
return

Niki

I haven't looked at this closely, but how about for a 16 bit
binary, do 16 bit tests and 16 appropriate BCD additions (using the DC
flag)?

Harold

Harold Hallikainen
haroldhallikainen.com
Hallikainen & Friends, Inc.
See the FCC Rules at http://hallikainen.com/FccRules and comments filed
in LPFM proceeding at http://hallikainen.com/lpfm

___________________________________________________________________
Get completely free e-mail from Juno at http://www.juno.com
or call Juno at (800) 654-JUNO [654-5866]

Hi Harold,

At 12:29 PM 10/15/98 EDT, you wrote:
>        I haven't looked at this closely, but how about for a 16 bit
>binary, do 16 bit tests and 16 appropriate BCD additions (using the DC
>flag)?

This is basically a more refined version of what I was talking about in
option #2 of my original message. This is probably faster than the
subtraction method, but it requires more space since it needs a lookup
table for the values to be added for each bit. There are ways to calculate
the values other than a lookup table, but I think that they would take even
more space.

Thanks,

Sean

{Quote hidden}

+-------------------------------+
| Sean Breheny                  |
| Electrical Engineering Student|
+-------------------------------+
Save lives, please look at http://www.all.org
Personal page: http://www.people.cornell.edu/pages/shb7
shb7cornell.edu  Phone(USA): (607) 253-0315 ICQ #: 3329174

part 0 1361 bytes
|It takes a while (6 loops max for 10ks, 10 loops max for the each of the
|other loops) but doesn't take too much space.  Otherwise, look for Johm
|Payson's neat routine that does it faster (much faster!) AND in less space!

Thanks for the plug.  I'm sure one of the Andys around here will
post my routine (who was the brilliant guy who commented the thing
far better than I could have done?)  Anyway, on a related note, I
was writing a program on an 8x51 clone (ducking) and needed to
display some 32-bit numbers; in some cases I needed a straight
integer output while in others I needed to display an integer # of
miliseconds in h:mm:ss.mmm format.  The techniques I used to do the
conversion may be of interest, though they were optimized to use the
8x51's DIV/MUL instructions: all divides and multiplies are by
constants, so table-lookup should be quite feasible.  I'll need to
clean the algorithm up if anyone is to understand it, so I'll post
it later.

For quick mod'ing by 10 or 6 of large numbers, there's a little
trick which may also be helpful: add up all the nybbles of the
number except the least-significant nybble, multiply that sum by
six, then add the least-significant nybble and mod by 10 or 6.
Since some quick-and-dirty divide-by-10 or divide-by-6 routines
don't report a remainder the quick mod'ing routine can be quite
handy at times.

On Tue, 20 Oct 1998, John Payson wrote:

>
> |It takes a while (6 loops max for 10ks, 10 loops max for the each of the
> |other loops) but doesn't take too much space.  Otherwise, look for Johm
> |Payson's neat routine that does it faster (much faster!) AND in less space!
>
> Thanks for the plug.  I'm sure one of the Andys around here will
> post my routine (who was the brilliant guy who commented the thing
> far better than I could have done?)

http://interstice.com/~sdattalo/technical/software/pic/bcd.txt

I would've posted the URL earlier, but the obligatory 3 month