Cannily realising that I'd want a binary to BCD conversion routine
in the future, I carefully filed and kept a brief thread on the subject
from a few weeks ago. Slightly less cannily, I can't understand what
it's doing. Can anyone point me in the right direction, pseudo-code-
wise? Let's say I have a byte 'count'; if it can run to FFh, I guess I
need 12 bytes of BCD (3 lots of 4 bytes?) to encode '255'. Or would
it be customary to stop at 63h (99 dec.)? Either way, in English (or some
approximation) what am I doing? This was the routine:
To me it looks more like a BCD to binary conversion. BCD=16*tens+ones. To
get it on to binary you require 10*tens+ones. so you do a BCD-8*tens+2*tens.
If you happen to get a binary to BCD converter, please forward it to be. I'm
really interested.
PS. It might be possible to get a binary to BCD converter using the same logic.
Am trying to work it out.
At 17:01 4/27/98 +0100, John Midgley wrote: {Quote hidden}
>Dear All
>
>Cannily realising that I'd want a binary to BCD conversion routine
>in the future, I carefully filed and kept a brief thread on the subject
>from a few weeks ago. Slightly less cannily, I can't understand what
>it's doing. Can anyone point me in the right direction, pseudo-code-
>wise? Let's say I have a byte 'count'; if it can run to FFh, I guess I
>need 12 bytes of BCD (3 lots of 4 bytes?) to encode '255'. Or would
>it be customary to stop at 63h (99 dec.)? Either way, in English (or some
>approximation) what am I doing? This was the routine:
>
>> rrf bcd,W
>> andlw 01111000b ;W = tens*8
>> movwf temp
>> clrc
>> rrf temp,f ;temp = tens*4
>> rrf temp,f ;temp = tens*2
>> subwf bcd,W ;W = tens*16 + ones - tens*8
>> ;W = tens*8 + ones
>> addwf temp,W ;W = tens*10 + ones
>>
>
>I know what all the opcodes mean, but that's not quite the same as
>understanding the whole thing!
>
>And if I wanted to go to 9999 decimal (270Fh) could I scale it up easily?
>
>Any help greatly appreciated.
>
>Regards
>
>John M
>
>
=================================================================
If love is in the air, then its polluted :-) :-)
=================================================================
>If you happen to get a binary to BCD converter, please forward it to be.
I'm
>really interested.
>
if cpu cycle are not critial you can try this routine. it converts a byte to
decimal 0 - 255. i just cut it out of an old project so i hope i got all the
part needed. if not please email me.
michael
;
; Convert a binary byte to ascii then to display segments
;
; Store binary data in 'W' and index points to first display area.
;
i_to_a
#define left Local_1
#define digit Local_2
#define power Local_3
#define blanking Local_bit_1
bsf blanking
movwf left
movlw 100
movwf power
call ia_label1
movlw 10
movwf power
call ia_label1
movlw 1
movwf power
bcf blanking
ia_label1
clrf digit
ia_loop movf power,w
subwf left,w
btfss STATUS,C
goto ia_end
movwf left
incf digit,f
goto ia_loop
ia_end movf digit,w
movwf indirect
btfss STATUS,Z ; is this digit zero?
goto ia_L1
btfss blanking ; is blanking set and digit zero
goto ia_L1
movlw " " ; load blank display segments
goto ia_L2
ia_L1 movlw 0x30
bcf blanking ; clear blanking flag
ia_L2 addwf indirect,f ; convert to ascii
incf index,f
return
>PS. It might be possible to get a binary to BCD converter using the
>same logic.
>Am trying to work it out.
The routine originally posted is indeed a BCD to binary converter. A
binary to BCD converter would be based on dividing by 10, rather than
multiplying. For single byte numbers, it is practical to repeatedly
subtract 10, counting the number of subtractions needed to reduce the
number to less than 10. The number of subtractions required is the tens
digit, and the remaining number is the ones digit.
The routine attached below is some not too elegant code from one of my
recent projects. It converts a number from 0 to 99 to two BCD digits
using a repeated subtraction method. The repeated subtraction method
becomes unweildy for numbers larger than 999. Either long division by 10
or the "BCD multiply by 2" routine should lead to smaller and faster
code.
;-------------
cvdec
; Converts the 1-byte number in bin to decimal. Number must be less than
100
; for correct result of course. Result returned as packed BCD in tmp.
; Number in bin is destroyed. (On exit, bin contains the ones digit with
the
; high 4 bits zero)
;
clrf tmp ;Start with result = 0.
movlw .10 ;Constant to use
cvdecl
incf tmp,f ;Inc. the tens digit.
subwf bin,f ;Subtract 10
skpnc ;Skip if bin was less than 10.
goto cvdecl ;Subtract again.
; Here with tens digit + 1 in low bits of bcd, and ones digit - 10
(negative)
; in bin.
addwf bin,f ;Restore ones digit (know now 0
to 9).
decf tmp,f ;Fix tens digit.
swapf tmp,w ;Get tens digit to high bits.
iorwf bin,w ;OR in the ones digit.
movwf tmp ;Put result back.
return
;----------------
_____________________________________________________________________
You don't need to buy Internet access to use free Internet e-mail.
Get completely free e-mail from Juno at http://www.juno.com
Or call Juno at (800) 654-JUNO [654-5866]
> Van: John Midgley <John.Midgleyspam_OUTENORFOLK-HA.ANGLOX.NHS.UK> >
> Aan: @spam@PICLISTKILLspamMITVMA.MIT.EDU
> Onderwerp: Binary to BCD Conversion
> Datum: maandag 27 april 1998 18:01
>
> Dear All
>
> Cannily realising that I'd want a binary to BCD conversion routine
> in the future, I carefully filed and kept a brief thread on the subject
> from a few weeks ago. Slightly less cannily, I can't understand what
> it's doing. Can anyone point me in the right direction, pseudo-code-
> wise? Let's say I have a byte 'count'; if it can run to FFh, I guess I
> need 12 bytes of BCD (3 lots of 4 bytes?) to encode '255'. Or would
I gather that you mean '3 lots of 4 _bits_' :-)
> it be customary to stop at 63h (99 dec.)? Either way, in English (or some
> approximation) what am I doing? This was the routine:
[Cut]
You will have heard by now that the above routine was a 2-digit packed
BCD-to-Binary routine ...
I've got a Binary-to-BCD routine using a long-tail division (homebrew) for
two bytes (resulting in 5 digits uncompressed BCD). If you are interrested
..
> And if I wanted to go to 9999 decimal (270Fh) could I scale it up easily?
If you are only counting (not converting the output of a calculation) you
could
opt for counting BCD-wise. said otherwise : increment the BCD-counter and
BCD-adjust the result.
> Any help greatly appreciated.
>
> Regards
>
> John M
> You will have heard by now that the above routine was a 2-digit packed
BCD-to-Binary routine ...
Oops again
> If you are only counting (not converting the output of a calculation) you
> could opt for counting BCD-wise. said otherwise : increment the BCD-
> counter and BCD-adjust the result.
I did ponder this one; I could do it either way, but I thought that the binary
to BCD conversion would be a solution that I'd use again.
> I've got a Binary-to-BCD routine using a long-tail division (homebrew) for
> two bytes (resulting in 5 digits uncompressed BCD). If you are interrested
I sure am. Thoroughly commented, I trust? I'm still trying to puzzle out how
it works. Horowitz and Hill just say 'It's complicated' which is not helpful. I'
ve
even drawn up a K-map and stared at it for a while. I stopped when I got a
headache.
If you want a quick program but slow running one,
I have seen where a one byte to bcd conversion
first subtracts 100 from the binary number,
and when it subtracted too much, it adds 100 back into
the binary number.
Then it subtracts 10 from the binary number until it goes
too far, then add 10 back in to binary number.
Then does the same for 1.
This may take 23 subtractions, so it is not fast running.
compare 200
if greater, subtract 200, add 2 to hundreds count
compare 100
if greater, subtract 100, add 1 to hundreds count
compare 80
if greater, subtract 80, add 8 to tens count
compare 40
if greater, subtract 40, add 4 to tens count
compare 20
if greater, subtract 20, add 2 to tens count
compare 10
if greater, subtract 10, add 1 to tens count
same for 'ones' and extending beyond to 16 bits or more. Hefty on code
space but fairly rapid. Although I've only tried it on a z8 so I don't
know how well it can be done on a Pic.
>
> If you want a quick program but slow running one,
> I have seen where a one byte to bcd conversion
> first subtracts 100 from the binary number,
> and when it subtracted too much, it adds 100 back into
> the binary number.
>
> Then it subtracts 10 from the binary number until it goes
> too far, then add 10 back in to binary number.
>
> Then does the same for 1.
>
> This may take 23 subtractions, so it is not fast running.
>
> Bill C. RemoveMEbillTakeThisOuTcornutt.com
----------------------------------------------------------------
Chris Savage spamBeGonechrisspamBeGonecti-vision.demon.co.uk
Software Engineer +44 385 396 993
CTIVision Ltd, Egham, UK
----------------------------------------------------------------
On Wed, 29 Apr 1998 14:21:20 +0100 Chris Savage
<TakeThisOuTchrisEraseMEspam_OUTCTI-VISION.DEMON.CO.UK> writes:
>Or how about, instead of repeated subtraction:
>
> compare 200
> if greater, subtract 200, add 2 to hundreds count
[....]
This is an interesting method. Maybe some automation could be done by
shifting the powers of 10 right for each digit. (i.e. 800, 200, 400,
100, then 80, 40, 20, 10). It still has to store a constant for each
power of 10 like the repeated subtraction method so it will use quite a
bit of code space for larger numbers. It's a little similar to long
division, but also an enhancement of repeated subtraction.
If speed is important, the "BCD multiply by 2" routine is likely best on
large numbers. It's also very compact since no powers of 10 are stored.
Long division also features compact size and easy expansion to large
numbers. The core of it can be re-used for other division tasks. With
conventional long division, all the compare and subtract operations are
with 10, so single-byte math can be used regardless of the size of the
input number. Repeated subtraction methods require multiple-byte math
once the input number is larger than 256.
I'll attempt to describe how long division can be applied to BCD
conversion:
Compute N = N/10. Remainder is a BCD digit from 0 to 9. Save the
remainders and repeat until N=0. The first remainder computed is the
rightmost (least significant) BCD digit.
Since the result is now zero the process is done. This inherently
suppresses leading zeros. If you'd rather have leading zeros, just
repeat the process a fixed number of times large enough to accomodate the
largest number expected. Subsequent steps will find 0/10 = 0, remainder
0.
If you're using an LCD panel the result can be written out "on the fly"
by configuring the panel to move the cursor right to left instead of as
normal left to right. Other forms of output such as RS-232 would require
saving the results in RAM then outputting them in proper MSD first order.
_____________________________________________________________________
You don't need to buy Internet access to use free Internet e-mail.
Get completely free e-mail from Juno at http://www.juno.com
Or call Juno at (800) 654-JUNO [654-5866]
----------
> Or how about, instead of repeated subtraction:
>
> compare 200
> if greater, subtract 200, add 2 to hundreds count
> compare 100
> if greater, subtract 100, add 1 to hundreds count
>
> compare 80
> if greater, subtract 80, add 8 to tens count
> compare 40
> if greater, subtract 40, add 4 to tens count
> compare 20
> if greater, subtract 20, add 2 to tens count
> compare 10
> if greater, subtract 10, add 1 to tens count
No need to do anything for 'ones' as the result of the above
subtractions will leave the ones value. But the test may should
be 'greater or equal'.
And a right shift of the 200 value gives a 100 value. The same
shifting 80 to get the next compare/subtraction value...
Is a shift quicker than a load?
Also if the 'test for 80' is true, then no need to 'test for 40'
> same for 'ones' and extending beyond to 16 bits or more. Hefty on code
> space but fairly rapid. Although I've only tried it on a z8 so I don't
> know how well it can be done on a Pic.
>
> ----------------------------------------------------------------
> Chris Savage chrisEraseME.....cti-vision.demon.co.uk
> Software Engineer +44 385 396 993
> CTIVision Ltd, Egham, UK
> ----------------------------------------------------------------
>
Bill Cornutt wrote:
>
> ----------
> No need to do anything for 'ones' as the result of the above
> subtractions will leave the ones value. But the test may should
> be 'greater or equal'.
Yer right. And I wrote the above without reference to my code sO I
forgot I'd done that. Honest.
>
> And a right shift of the 200 value gives a 100 value. The same
> shifting 80 to get the next compare/subtraction value...
> Is a shift quicker than a load?
>
> Also if the 'test for 80' is true, then no need to 'test for 40'
On Thu, 30 Apr 1998 08:37:34 +0100 Chris Savage
<RemoveMEchrisspam_OUTKILLspamcti-vision.demon.co.uk> writes:
>Can you point me to an elaboeation of this method, please. Can't find
>anything in the archive.
A version of the routine can be found in the Microchip application note
AN526. An 8-bit routine based on repeated subtraction is presented, then
a 16-bit "BCD multiply by 2" routine. There isn't a lot of explanation
there, but it does work. The key to the routine is "adjusting" a packed
BCD number so shifting it left one bit multiplies it by 2. If a digit is
more than 4, 3 is added to it:
Original After adj After shift
0 0 0
1 1 2
2 2 4
3 3 6
4 4 8
5 8 (1) 0
6 9 (1) 2
7 A (1) 4
8 B (1) 6
9 C (1) 8
As you can see, the adjustment process makes it simple to multiply by 2
in BCD. With this ability, the binary number is shifted into the BCD
number one bit at a time. The MSB of the binary number is shifted into
the LSB of the adjusted BCD number, so the new value of BCD is either
BCD*2 or BCD*2+1 depending on the value of the binary bit. After doing
all the bits, the BCD number is complete. It is simple to expand the
routine's ability to larger numbers by adding more RAM and doing more
shifts.
The Microchip routine can be optimized some. I posted a complete
optimized routine a while ago, but I can't find it now either. Here it
is again. The routine is for 24 bits being converted to 8 digits. Both
numbers are in "little-endian" format: LSB first in memory. The routine
tests a bit in FSR to control the inner loop, requiring 'bcd' to be at a
specific place in RAM (like 0C to 0F).
; Rewrite of b2bcd for less space; inlined it.
;b2bcd
movlw d'24'
movwf ii
clrf bcd ;clear result to all 0.
clrf bcd+1
clrf bcd+2
clrf bcd+3
b2bcdl
movlw bcd ;Point at first bcd
movwf FSR
; Copy of 'adjbcd'. OK to adjust the first time before shifting.
b2bcdil
movlw h'33'
addwf INDF,f ;Add to low and high nybbles
btfsc INDF,3
andlw h'f0' ;Low result >7 . OK (take the 3
out)
btfsc INDF,7
andlw h'0f' ;Hi > 7 OK.
subwf INDF,f ;any results <=7, subtract
back.
incf FSR,f ;Inc. pointer for next time
; bcd placed at known address, so bits in FSR could be used for loop
control
btfss FSR,4 ;When FSR reaches 0x10, done.
goto b2bcdil ;If not done, do the inner
loop again.
rlf bin+0,f
rlf bin+1,f
rlf bin+2,f ;Get another bit out of bin.
rlf bcd+0,f ;Put bit into bcd.
rlf bcd+1,f
rlf bcd+2,f
rlf bcd+3,f
decfsz ii,f ;Do more?
goto b2bcdl ;Yes.
_____________________________________________________________________
You don't need to buy Internet access to use free Internet e-mail.
Get completely free e-mail from Juno at http://www.juno.com
Or call Juno at (800) 654-JUNO [654-5866]
Improving the method discussed earlier:
taking X=byte and converting to BCD digits H,T,U
in pseudoBasic!!
H=0
T=0
U=X
if U>=200 then H=H+2: U=U-200: Goto Check40 'NUMBER WILL BE <=55
if U>=100 then H=H+1: U=U-100
if U>=80 then T=T+8: U=U-80: Goto Check10 'NUMBER WILL BE <=19
Check40:
if U>=40 then T=T+4: U=U-40
if U>=20 then T=T+2: U=U-20
Check10:
if U>=10 then T=T+1: U=U-10
should take 24 to 48 instructions on a pic depending on X
This reminds me of a game I use to play with a fellow
programmer. When we had a bug we would look it over
and then play the game "Fix That Bug", based on the
game "Name That Tune".
"I can fix that bug in five bytes."
"I can fix that bug in four bytes."
"Fix that bug!"
> At 10:49 30/04/98 -0400, you wrote:
>
>
> Improving the method discussed earlier:
> taking X=byte and converting to BCD digits H,T,U
>
> in pseudoBasic!!
>
> H=0
> T=0
> U=X
> if U>=200 then H=H+2: U=U-200: Goto Check40 'NUMBER WILL BE <=55
> if U>=100 then H=H+1: U=U-100
> if U>=80 then T=T+8: U=U-80: Goto Check10 'NUMBER WILL BE <=19
> Check40:
> if U>=40 then T=T+4: U=U-40
> if U>=20 then T=T+2: U=U-20
> Check10:
> if U>=10 then T=T+1: U=U-10
>
> should take 24 to 48 instructions on a pic depending on X
>
> _____________________________________________________
> Osama ALASSIRY osamaSTOPspamspam_OUTqatar.net.qaspamBeGoneosamaSTOPspamEraseMEalassiry.com
> http://www.alassiry.com
>
> And a right shift of the 200 value gives a 100 value. The same
> shifting 80 to get the next compare/subtraction value...
> Is a shift quicker than a load?
If the value is in a file register and the carry is already clear,
yes. This is particularly useful if you extend this procedure to multi-
byte values.
> Also if the 'test for 80' is true, then no need to 'test for 40'
> Improving the method discussed earlier:
> taking X=byte and converting to BCD digits H,T,U
>
> H=0
> T=0
> U=X
> if U>=200 then H=H+2: U=U-200: Goto Check40 'NUMBER WILL BE <=55
> if U>=100 then H=H+1: U=U-100
> if U>=80 then T=T+8: U=U-80: Goto Check10 'NUMBER WILL BE <=19
> Check40:
> if U>=40 then T=T+4: U=U-40
> if U>=20 then T=T+2: U=U-20
> Check10:
> if U>=10 then T=T+1: U=U-10
If you are using fixed subtractions rather than shifts, and since each
"IF" is coded <skip conditional> <GOTO else code> <IF code> (<GOTO ...>)
<else code> then the one-instruction-less optimisation of the above is:
H=0: T=0: U=X
C200:
if U=>200 goto CG200
C100:
if U<100 goto C80
H=H+1: U=U-100
C80:
if U<80 goto C40
T=T+8: U=U-80
goto C10
CG200:
H=H+2: U=U-200
C40:
if U<40 goto C20
T=T+4: U=U-40
C20:
if U<20 goto C10
T=T+2: U=U-20
C10:
if U<10 goto C00
T=T+1: U=U-10
C00:
> If you are using fixed subtractions rather than shifts, and since each
> "IF" is coded <skip conditional> <GOTO else code> <IF code> (<GOTO ...>)
> <else code> then the one-instruction-less optimisation of the above is:
>
<snip>
>
> FWIW!
about 33 instructions. However, to up the 1-byte less ante, there's a
trick that allows you to use binary comparisons for a 28-cycle conversion.
I divulge those tricks tomorrow...
However for the decimal version, this little trick works well:
MOVF bin,W
ADDLW -200
RLF tens_and_ones,F ;Get the (inverted) carry
BTFSS tens_and_ones,0 ;If there was no carry
ADDLW 200 ;put back the 200
. . .
Followed later by a COMF (or some other bit inverting trick) to get
the proper polarity.
(This link is from the archive and takes a while to download.
It's been a year since it's been posted, perhaps it's time
again?)
Here's the 28-cycle (non-looping) version that I promised yesterday.
It's based on binary comparisons. It's one of those routines that
is very difficult to comment. So I didn't. However it takes advantage
of this little trick to quickly ascertain the ones' digit:
If you look at the ones' digit for 2^N you see this pattern:
n = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 ...
2^n % 10 = 1, 2, 4, 8, 6, 2, 4, 8, 6, 2, 4, 8 ...
If it wasn't for the annoying 6's, you could simply sum the nibbles to
get and get the ones' digit (after a relatively simple binary to BCD
conversion). However, it's simple enough to test if bit 4, 8,...
are high and to subtract 1 and then add 6 (or simply add 5) if they
are.
The second observation is that the sum of all of the tens' digits is
less than 16, and thus can fit into one nibble. This simplifies having
to deal with overflow until after all of the digits have been added
together.
The third observation is that the BCD result is greater than 200 only
if the most significant bit is set.
;********************************
;binary_to_bcd - 8-bits
;
;Input
; bin - 8-bit binary number
;Outputs
; hundreds - the hundreds digit of the BCD conversion
; tens_and_ones - the tens and ones digits of the BCD conversion
I think the only real "problem" with regard to optimisation, is that
there are still seven major comparisons here, six in the shortest path,
and the execution of the above takes three adjustments to variables.
Also, when comparing these steps, it is as well to remember that
adding or subtracting one to (or doubling) a variable is one
instruction, but any other number requires two.
Have I missed something clever, but I don't see why you specified
"while" loops?
>
> > while(U.bit7) /*if bit7, the definately
> > 128 */
> > { U=U-120
> > H=H+1
> > T=T+2
>
> I think the only real "problem" with regard to optimisation, is that
> there are still seven major comparisons here, six in the shortest path,
> and the execution of the above takes three adjustments to variables.
>
> Also, when comparing these steps, it is as well to remember that
> adding or subtracting one to (or doubling) a variable is one
> instruction, but any other number requires two.
>
> Have I missed something clever, but I don't see why you specified
> "while" loops?
Because Sujay 'compares' 2^n but subtracts (2^n - (2^n % 10)). So
for 255, you check the MSB and say 'yeah, it's greater than 127 thus
I'll subtract 120'. However, this still leaves the MSB set. Hence the
while.
My only comment about the code is, well, I don't think it can solve
the problem in 5 bytes.
bin2bcd:
clrf bcdto ;clear tens and ones
clrf bcdh ;clear hundreds
movlw 8 ;8 bits to process
movwf cnt
loop rrf bin ;get lsb
skpnc ;compare if set
call bcd_add ; add
decfsz cnt ;loop for 8 times
goto loop
skpnc ;if msb is set add 1 in hundred
incf bcdh ; 'coz last constant is only 28
return
As a novice, I studied the "AN-526". Tried to run the program "Appendix H: Binary (16bit) to BCD".
However, the result in R0, R1, R2 become 06,85,53 in stead of 06,55,35 when converting the number B'11111111' in both H_byte & L_byte.
I traced every step but can't find where is the problem?
Not yet understand the algorithm of such a conversion, my first step is to follow the codes; even so the result is not correct.
The sample routine was publilshed many years ago, there must be many people ever study it. I would appreciate if someone hints me the blind point(s) where I stuck?