Converting BCD byte values to decimal

hippy

Technical Support
Staff member
#1
Here is an elegant way to convert a BCD byte value in 'b0' into its decimal equivalent in 'b0', or into another variable if the original value of 'b0' has to be retained ...

b0 = b0 / 16 * MINUS_6 + b0


This uses less code space, is faster, and requires no additional variable as the more commonly used conversion code does ...

b1 = b0 / 16 * 10
b0 = b0 & 15 | b1


Demo code which runs in the simulator and real chip to show that it works follows ...

Code:
b0 = $09 : Gosub Convert
b0 = $18 : Gosub Convert
b0 = $27 : Gosub Convert
b0 = $36 : Gosub Convert
b0 = $45 : Gosub Convert
b0 = $54 : Gosub Convert
b0 = $63 : Gosub Convert
b0 = $72 : Gosub Convert
b0 = $81 : Gosub Convert
b0 = $90 : Gosub Convert
End

Symbol MINUS_6 = $10000 - 6

Convert:
  b0 = b0 / 16 * MINUS_6 + b0
  SerTxd( #b0, " " )
  Return
Code:
For b1 = $00 to $90 Step $10
  For b2 = $0 To $9
    b0 = b1 | b2 : Gosub Convert
  Next
Next
End

Symbol MINUS_6 = $10000 - 6

Convert:
  b0 = b0 / 16 * MINUS_6 + b0
  SerTxd( #b0, " " )
  Return
The trick behind the conversion is thinking in a different way to trying to create the decimal value from the two BCD nibbles individually; how would one need to adjust the decimal value of the BCD number ( as it is ) to get what one actually wants ... ?

$10 = 16, should be 10, so need to subtract 6
$20 = 32, should be 20, so need to subtract 12
$30 = 48, should be 30, so need to subtract 18
$40 = 64, should be 40, so need to subtract 24
$50 = 80, should be 50, so need to subtract 30
etc

There is a very obvious and convenient pattern; the number we need to subtract is the most significant BCD nibble multiplied by six. What we would like to use as a conversion expression is ...

b0 = b0 - ( (b0/16) * 6 )


That however will not pass syntax check so we need to rearrange it to something which does.

First -(a*b) can equally be represented as a*(-b) so ...

b0 = b0 - ( (b0/16) * 6 )

becomes

b0 = b0 + ( (b0/16) * -6 )


Addition is commutative, a+b is the same as b+a, so ...

b0 = b0 + ( (b0/16) * -6 )

becomes

b0 = ( (b0/16) * -6 ) + b0


And now the precedence of operation there matches the left to right nature of PICAXE maths, so we can simply take out the parenthesis ( round brackets ) ...

b0 = b0 / 16 * -6 + b0


Unfortunately it is not possible to just place "-6" in the middle of an assignment expression on a PICAXE without a syntax error but that "-6" can be replaced by something defined by a SYMBOL statement so we end up with ...

b0 = b0 / 16 * MINUS_6 + b0


Defining MINUS_6 in a SYMBOL statement is a bit of a challenge if one wants to do it in a single line. First "-6" is not acceptable syntax by itself, and while "0-6" is valid and creates the desired value, that generates a 32-bit number so needs to be masked with $FFFF to limit it to 16-bits for use with PICAXE maths. Unfortunately the SYMBOL only allows one operator per SYMBOL so this would have to be done in two steps with two SYMBOL statements.

One pragmatic solution is to calculate -6 by hand ($FFFA) and just drop that into the single SYMBOL statement ...

Symbol MINUS_6 = $FFFA ; -6 = 0 - 6 & $FFFF

Another is to force 16-bit compliance not by subtracting 6 from 0 but from $10000 ...

Symbol MINUS_6 = $10000 - 6

However the MINUS_6 constant is created it has no impact on the expression code size or execution speed; both create a value of $FFFA or -6 for 16-bit maths use.
 

marks

Senior Member
#2
There really should be more Code Snippets like this !
thanks hippy
Great test code as usual too ...
Code:
For b1 = $00 to $90 Step $10
  For b2 = $0 To $9
    b0 = b1 | b2 : Gosub Convert
  Next
Next
Stop

Convert:
   b0 = b0 / 16 * 250 + b0 ' and using a smaller number may save a byte or two 
  
  'b0 = bcdtobin b0        ' lets not forget X1 and X2's
  
  SerTxd(#b0," ")
  Return
 

hippy

Technical Support
Staff member
#5
I have been meaning to update this for a while as ( I think buzby ) noted that, because it is a byte to byte conversion, the MINUS_6 can simply be $FA which gives the same byte result as multiplying by $FFFA.

So, to convert a BCD ($12) byte to binary decimal (12) ...

bin = bcd / 16 * $FA + bcd

And to convert a binary decimal (12) byte to BCD ($12) ...

bcd = bin / 10 * 6 + bin
 
Top